diff --git a/.maestro/enrichedInput/flows/custom_style_colors_visual.yaml b/.maestro/enrichedInput/flows/custom_style_colors_visual.yaml new file mode 100644 index 000000000..04ad337d1 --- /dev/null +++ b/.maestro/enrichedInput/flows/custom_style_colors_visual.yaml @@ -0,0 +1,118 @@ +appId: swmansion.enriched.example +--- +# Validates custom colors on plain text, inline styles, and paragraph styles. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'editor-input' + +# Section 1: Plain text with colors + +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-FF0000' +- inputText: 'Red text' +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-clear' +- inputText: ' ' + +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-FFFF00' +- inputText: 'Yellow back' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-clear' +- inputText: ' ' + +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-FF0000' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-FFFF00' +- inputText: 'Red+Yellow' +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-clear' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-clear' +- inputText: ' ' + +# Section 2: Inline styles + color + +- tapOn: + id: 'toolbar-bold' +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-FF0000' +- inputText: 'Bold red' +- tapOn: + id: 'toolbar-bold' +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-clear' +- inputText: ' ' + +- tapOn: + id: 'toolbar-italic' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-FFFF00' +- inputText: 'Italic yellow back' +- tapOn: + id: 'toolbar-italic' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-clear' +- inputText: ' ' +- pressKey: Enter + +# Section 3: Paragraph styles + color + +- tapOn: + id: 'toolbar-heading-5' +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-FF0000' +- inputText: 'H5 red' +- pressKey: Enter +- tapOn: + id: 'toolbar-text-color' +- tapOn: + id: 'color-swatch-clear' + +- tapOn: + id: 'toolbar-quote' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-FFFF00' +- inputText: 'Quote yellow back' +- tapOn: + id: 'toolbar-bg-color' +- tapOn: + id: 'color-swatch-clear' + +- runFlow: + file: '../subflows/capture_or_assert_screenshot.yaml' + env: + SCREENSHOT_NAME: 'custom_style_colors' diff --git a/.maestro/enrichedInput/screenshots/android/codeblock_no_link_detection.png b/.maestro/enrichedInput/screenshots/android/codeblock_no_link_detection.png index 22acdaf60..78e27577d 100644 Binary files a/.maestro/enrichedInput/screenshots/android/codeblock_no_link_detection.png and b/.maestro/enrichedInput/screenshots/android/codeblock_no_link_detection.png differ diff --git a/.maestro/enrichedInput/screenshots/android/codeblock_style_blocking.png b/.maestro/enrichedInput/screenshots/android/codeblock_style_blocking.png index ad4efbb28..44ae70724 100644 Binary files a/.maestro/enrichedInput/screenshots/android/codeblock_style_blocking.png and b/.maestro/enrichedInput/screenshots/android/codeblock_style_blocking.png differ diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png index 75979ebea..4d075df95 100644 Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png differ diff --git a/.maestro/enrichedInput/screenshots/ios/custom_style_colors.png b/.maestro/enrichedInput/screenshots/ios/custom_style_colors.png new file mode 100644 index 000000000..e8e39c715 Binary files /dev/null and b/.maestro/enrichedInput/screenshots/ios/custom_style_colors.png differ diff --git a/.maestro/enrichedText/flows/custom_style_colors_visual.yaml b/.maestro/enrichedText/flows/custom_style_colors_visual.yaml new file mode 100644 index 000000000..e304b5b94 --- /dev/null +++ b/.maestro/enrichedText/flows/custom_style_colors_visual.yaml @@ -0,0 +1,28 @@ +appId: swmansion.enriched.example +--- +# Validates that custom style colors are displayed correctly +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'toggle-enriched-text-screen-button' + +- runFlow: + file: '../subflows/set_enriched_text_value.yaml' + env: + VALUE: > + +
Standard 6-digit Hex text
+White text on black background
+25% transparent green background
+50% transparent blue text
+Red 3-digit shorthand text
+tags the same way lists use
x', '
x
x
', 'x
'], + ['x
', 'x
'], + ['x
', 'x
'], + ])('%s → %s', (input, expected) => { + expect(normalizeHtml(input)).toBe(expected); + }); + }); + + describe('TableOmissions', () => { + test.each([ + ['| Emil | Tobias | Linus |
Emil Tobias Linus
', + ], + [ + '| Emil | Tobias | Linus |
| 16 | 14 | 10 |
Emil Tobias Linus
16 14 10
', + ], + [ + '| Person 1 | Person 2 | Person 3 |
|---|---|---|
| Emil | Tobias | Linus |
| 16 | 14 | 10 |
Person 1 Person 2 Person 3
Emil Tobias Linus
16 14 10
', + ], + ])('%s → %s', (input, expected) => { + expect(normalizeHtml(input)).toBe(expected); + }); + }); + + describe('SpanRemappings', () => { + test.each([ + // Bold + ['x', 'x'], + ['x', 'x'], + ["x", 'x'], + + // Italic + ['x', 'x'], + ['x', 'x'], + ["x", 'x'], + + // Underline + ['x', 'x'], + ['x', 'x'], + ["x", 'x'], + + // Strikethrough + ['x', 'x
x
x
x', '
'], + [ + 'x
', + 'x
', + ], + + // Headings + ['x
x', 'x'],
+ ['x
'], + ['x
x
'], + ['x
y
x
y
'], + ['x
'], + [ + 'x
y
', + ], + + // Without whitespace + [ + '----
John Doe
Software Engineer
', + ], + [ + 'John Doe
Software Engineer
', + ], + [ + "----
John Doe
Software Engineer
', + ], + [ + "----
John Doe
Software Engineer
', + ], + [ + "----
John Doe
Software Engineer
', + ], + [ + "----
John Doe
Software Engineer
', + ], + + [ + '
',
+ 'here's more!

', + '
- another one hello
hi
what do you think of this craziness
', + ], + ])('%s → %s', (input, expected) => { + expect(normalizeHtml(input)).toBe(expected); + }); + }); + + describe('ListFlattening', () => { + test.each([ + [ + 'another one hello
hi
Asdasdasd
Sent with Net
" + ) + ).toBe( + 'Asdasdasd
Sent with Net
' + ); + }); + }); + + describe('character escaping', () => { + // Each special character in text content is re-emitted as its entity. + test.each([ + ['a & b
', 'a & b
'], + ['a < b
', 'a < b
'], + ['a > b
', 'a > b
'], + ['"quoted"
', '"quoted"
'], + ["it's
", 'it's
'], + ['&<>"\'
', '&<>"'
'], + ['&<>"\'
', '&<>"'
'], + [ + 'x', + 'x', + ], + ])('%s → %s', (input, expected) => { + expect(normalizeHtml(input)).toBe(expected); + }); + }); +}); diff --git a/src/web/checkboxHtmlNormalizer.ts b/src/web/normalization/checkboxHtmlNormalizer.ts similarity index 100% rename from src/web/checkboxHtmlNormalizer.ts rename to src/web/normalization/checkboxHtmlNormalizer.ts diff --git a/src/web/normalization/htmlNormalizer.ts b/src/web/normalization/htmlNormalizer.ts new file mode 100644 index 000000000..be34e44f9 --- /dev/null +++ b/src/web/normalization/htmlNormalizer.ts @@ -0,0 +1,609 @@ +/* + * Custom HTML normalizer for TipTap input. + * Mirrors the native GumboNormalizer (cpp/parser/GumboNormalizer.c) + */ + +type CssStyles = { + bold: boolean; + italic: boolean; + underline: boolean; + strikethrough: boolean; +}; + +const INLINE_TAGS = new Set([ + 'b', + 'i', + 'u', + 's', + 'code', + 'a', + 'strong', + 'em', + 'del', + 'strike', + 'ins', + 'mention', +]); + +const BLOCK_TAGS = new Set([ + 'p', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'ul', + 'ol', + 'li', + 'blockquote', + 'codeblock', + 'pre', +]); + +const SELF_CLOSING_TAGS = new Set(['br', 'img']); + +const PASS_TAGS = new Set(['html', 'head', 'body']); + +const STRIPPED_TAGS = new Set(['meta', 'style', 'script', 'title', 'link']); + +const TABLE_TAGS = new Set([ + 'table', + 'thead', + 'tbody', + 'tfoot', + 'tr', + 'td', + 'th', + 'caption', + 'colgroup', + 'col', +]); + +function canonicalName(name: string): string { + switch (name) { + case 'strong': + return 'b'; + case 'em': + return 'i'; + case 'del': + case 'strike': + return 's'; + case 'ins': + return 'u'; + case 'pre': + return 'codeblock'; + default: + return name; + } +} + +type TagClass = 'skip' | 'inline' | 'block' | 'self-closing' | 'pass'; + +function classifyTag(name: string): TagClass { + if (INLINE_TAGS.has(name)) return 'inline'; + if (BLOCK_TAGS.has(name)) return 'block'; + if (SELF_CLOSING_TAGS.has(name)) return 'self-closing'; + if (PASS_TAGS.has(name)) return 'pass'; + return 'skip'; +} + +function isElement(node: Node | null): node is Element { + return !!node && node.nodeType === Node.ELEMENT_NODE; +} + +function isText(node: Node | null): boolean { + return !!node && node.nodeType === Node.TEXT_NODE; +} + +function tagName(node: Node | null): string | null { + if (!isElement(node)) return null; + return node.tagName.toLowerCase(); +} + +function isListNode(node: Node | null): boolean { + const n = tagName(node); + return n === 'ul' || n === 'ol'; +} + +function isBlockquoteNode(node: Node | null): boolean { + return tagName(node) === 'blockquote'; +} + +function isBrNode(node: Node | null): boolean { + return tagName(node) === 'br'; +} + +function isBlockProducing(node: Node | null): boolean { + const n = tagName(node); + if (!n) return false; + if (classifyTag(n) === 'block') return true; + return n === 'div' || n === 'table' || n === 'tr'; +} + +function isPurelyInline(node: Element): boolean { + for (const child of Array.from(node.childNodes)) { + if (isBlockProducing(child)) return false; + } + return true; +} + +function hasBlockOrBqChild(node: Element): boolean { + for (const child of Array.from(node.childNodes)) { + if (isBlockProducing(child) || isBlockquoteNode(child)) return true; + } + return false; +} + +function findCssValue(style: string, prop: string): string | null { + // Returns the value of the last declaration with this property name. + // Mirrors GumboNormalizer's find_css_value scanning behavior. + const re = new RegExp( + `(?:^|;)\\s*${prop.replace(/[-/\\^$*+?.()|[\\]{}]/g, '\\$&')}\\s*:\\s*([^;]*)`, + 'i' + ); + + const m = re.exec(style); + if (m !== null) { + return (m[1] ?? '').trim(); + } + return null; +} + +function findAllCssValues(style: string, prop: string): string[] { + const re = new RegExp( + `(?:^|;)\\s*${prop.replace(/[-/\\^$*+?.()|[\\]{}]/g, '\\$&')}\\s*:\\s*([^;]*)`, + 'gi' + ); + const out: string[] = []; + let m: RegExpExecArray | null; + while ((m = re.exec(style)) !== null) { + out.push((m[1] ?? '').trim()); + } + return out; +} + +function parseCssStyle(style: string | null): CssStyles { + const result: CssStyles = { + bold: false, + italic: false, + underline: false, + strikethrough: false, + }; + if (!style) return result; + + const fw = findCssValue(style, 'font-weight'); + if (fw) { + const lower = fw.toLowerCase(); + if (lower.includes('bold') || lower.includes('bolder')) { + result.bold = true; + } else { + const num = parseInt(fw, 10); + if (!Number.isNaN(num) && num >= 700) result.bold = true; + } + } + + const fs = findCssValue(style, 'font-style'); + if (fs) { + const lower = fs.toLowerCase(); + if (lower.includes('italic') || lower.includes('oblique')) + result.italic = true; + } + + // text-decoration / text-decoration-line: scan ALL declarations + const decorations = [ + ...findAllCssValues(style, 'text-decoration-line'), + ...findAllCssValues(style, 'text-decoration'), + ]; + for (const v of decorations) { + const lower = v.toLowerCase(); + if (lower.includes('underline')) result.underline = true; + if (lower.includes('line-through')) result.strikethrough = true; + } + + return result; +} + +function extraStyles(s: CssStyles, tag: string): CssStyles { + return { + bold: s.bold && tag !== 'b', + italic: s.italic && tag !== 'i', + underline: s.underline && tag !== 'u', + strikethrough: s.strikethrough && tag !== 's', + }; +} + +function emitStylesOpen(s: CssStyles): string { + let out = ''; + if (s.bold) out += ''; + if (s.italic) out += ''; + if (s.underline) out += ''; + if (s.strikethrough) out += '${ib.buf}
`; + ib.buf = ''; + } +} + +function flattenBqChildren( + node: Element, + ib: { buf: string }, + out: { buf: string } +): void { + for (const child of Array.from(node.childNodes)) { + flattenBqNode(child, ib, out); + } +} + +function flattenBqNode( + node: Node, + ib: { buf: string }, + out: { buf: string } +): void { + if (isText(node)) { + const t = node.textContent ?? ''; + ib.buf += escapeText(t); + return; + } + if (!isElement(node)) return; + if (isBrNode(node)) { + flushInlineP(ib, out); + return; + } + if (isBlockProducing(node) || isBlockquoteNode(node)) { + flushInlineP(ib, out); + flattenBqChildren(node, ib, out); + flushInlineP(ib, out); + return; + } + walkNode(node, ib); +} + +// --- List item content flattening --- + +type LiCtx = { + el: Element; + styles: CssStyles; + nestedLists: Element[]; +}; + +function flushLiBuffer( + ib: { buf: string }, + out: { buf: string }, + ctx: LiCtx +): void { + if (ib.buf.length === 0) return; + out.buf += `'; + const bqIb = { buf: '' }; + while (i < children.length) { + const cur = children[i]; + if (!cur || !isElement(cur) || !isBlockquoteNode(cur)) break; + flattenBqChildren(cur, bqIb, out); + i++; + } + flushInlineP(bqIb, out); + out.buf += ''; + continue; + } + + // Auto-paragraph: group inline runs into
when mixed with blocks
+ if (
+ hasBlock &&
+ !parentIsList &&
+ !isBlockProducing(child) &&
+ !(isElement(child) && isBlockquoteNode(child))
+ ) {
+ const ib = { buf: '' };
+ while (i < children.length) {
+ const cur = children[i]!;
+ if (
+ isBlockProducing(cur) ||
+ (isElement(cur) && isBlockquoteNode(cur))
+ ) {
+ break;
+ }
+ if (isElement(cur) && isBrNode(cur)) {
+ if (ib.buf.length > 0) {
+ flushInlineP(ib, out);
+ } else {
+ out.buf += ' or passes through
+ if (name === 'div') {
+ const s = parseCssStyle(node.getAttribute('style'));
+ if (isPurelyInline(node)) {
+ const pb = { buf: '' };
+ for (const dc of Array.from(node.childNodes)) {
+ if (isElement(dc) && isBrNode(dc)) {
+ if (pb.buf.length > 0) {
+ out.buf += ` ${emitStylesOpen(s)}${pb.buf}${emitStylesClose(s)} ${emitStylesOpen(s)}${pb.buf}${emitStylesClose(s)} ${row.buf}
+ if (outName === 'codeblock') {
+ const wrap = isPurelyInline(node);
+ out.buf += ' ';
+ walkChildren(node, out);
+ if (wrap) out.buf += '
';
+ }
+ i++;
+ continue;
+ }
+ // Transparent inline wrapper for block/bq children
+ if (isElement(cur) && hasBlockOrBqChild(cur)) {
+ flushInlineP(ib, out);
+ walkChildren(cur, out);
+ i++;
+ continue;
+ }
+ walkNode(cur, ib);
+ i++;
+ }
+ flushInlineP(ib, out);
+ continue;
+ }
+
+ walkNode(child, out);
+ i++;
+ }
+}
+
+function walkNode(node: Node, out: { buf: string }): void {
+ if (isText(node)) {
+ out.buf += escapeText(node.textContent ?? '');
+ return;
+ }
+ if (!isElement(node)) return;
+
+ const name = node.tagName.toLowerCase();
+
+ if (STRIPPED_TAGS.has(name)) return;
+
+ if (isGoogleDocsWrapper(node, name)) {
+ walkChildren(node, out);
+ return;
+ }
+
+ const outName = canonicalName(name);
+ const cls = classifyTag(name);
+
+ // : CSS style → inline tags
+ if (name === 'span') {
+ const s = parseCssStyle(node.getAttribute('style'));
+ out.buf += emitStylesOpen(s);
+ walkChildren(node, out);
+ out.buf += emitStylesClose(s);
+ return;
+ }
+
+ //
';
+ }
+ pb.buf = '';
+ continue;
+ }
+ walkNode(dc, pb);
+ }
+ if (pb.buf.length > 0) {
+ out.buf += `
/gi, '');
return html;
diff --git a/src/web/useOnChangeHtml.ts b/src/web/useOnChangeHtml.ts
index 8953cec95..90d159d97 100644
--- a/src/web/useOnChangeHtml.ts
+++ b/src/web/useOnChangeHtml.ts
@@ -2,7 +2,7 @@ import { type Editor } from '@tiptap/react';
import type { OnChangeHtmlEvent } from '../types';
import type { NativeSyntheticEvent } from 'react-native';
import { useOnEditorChange } from './useOnEditorChange';
-import { normalizeHtmlFromTiptap } from './tiptapHtmlNormalizer';
+import { normalizeHtmlFromTiptap } from './normalization/tiptapHtmlNormalizer';
export const useOnChangeHtml = (
editor: Editor,
diff --git a/src/web/useOnChangeState.ts b/src/web/useOnChangeState.ts
index 439f685de..6f4fcb175 100644
--- a/src/web/useOnChangeState.ts
+++ b/src/web/useOnChangeState.ts
@@ -98,6 +98,10 @@ function buildState(
isBlocking: isFormatBlocked('image', editor, htmlStyle),
},
alignment: 'left',
+ customStyle: {
+ foregroundColor: '',
+ backgroundColor: '',
+ },
};
}
diff --git a/yarn.lock b/yarn.lock
index 46bce3472..3422547d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4304,6 +4304,13 @@ __metadata:
languageName: node
linkType: hard
+"@tootallnate/once@npm:2":
+ version: 2.0.1
+ resolution: "@tootallnate/once@npm:2.0.1"
+ checksum: 10c0/23b01a341485be711c602077936d70f8e695405bb88ab4433dc6d1e6cb4556401518789574d399eded790b70b27738136c9a8f02df7ae4219f4ba28bb22d586b
+ languageName: node
+ linkType: hard
+
"@tootallnate/quickjs-emscripten@npm:^0.23.0":
version: 0.23.0
resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0"
@@ -4419,6 +4426,17 @@ __metadata:
languageName: node
linkType: hard
+"@types/jsdom@npm:^20.0.0":
+ version: 20.0.1
+ resolution: "@types/jsdom@npm:20.0.1"
+ dependencies:
+ "@types/node": "npm:*"
+ "@types/tough-cookie": "npm:*"
+ parse5: "npm:^7.0.0"
+ checksum: 10c0/3d4b2a3eab145674ee6da482607c5e48977869109f0f62560bf91ae1a792c9e847ac7c6aaf243ed2e97333cb3c51aef314ffa54a19ef174b8f9592dfcb836b25
+ languageName: node
+ linkType: hard
+
"@types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.9":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
@@ -4523,6 +4541,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/tough-cookie@npm:*":
+ version: 4.0.5
+ resolution: "@types/tough-cookie@npm:4.0.5"
+ checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473
+ languageName: node
+ linkType: hard
+
"@types/use-sync-external-store@npm:^0.0.6":
version: 0.0.6
resolution: "@types/use-sync-external-store@npm:0.0.6"
@@ -4897,6 +4922,13 @@ __metadata:
languageName: node
linkType: hard
+"abab@npm:^2.0.6":
+ version: 2.0.6
+ resolution: "abab@npm:2.0.6"
+ checksum: 10c0/0b245c3c3ea2598fe0025abf7cc7bb507b06949d51e8edae5d12c1b847a0a0c09639abcb94788332b4e2044ac4491c1e8f571b51c7826fd4b0bda1685ad4a278
+ languageName: node
+ linkType: hard
+
"abbrev@npm:^3.0.0":
version: 3.0.1
resolution: "abbrev@npm:3.0.1"
@@ -4933,6 +4965,16 @@ __metadata:
languageName: node
linkType: hard
+"acorn-globals@npm:^7.0.0":
+ version: 7.0.1
+ resolution: "acorn-globals@npm:7.0.1"
+ dependencies:
+ acorn: "npm:^8.1.0"
+ acorn-walk: "npm:^8.0.2"
+ checksum: 10c0/7437f58e92d99292dbebd0e79531af27d706c9f272f31c675d793da6c82d897e75302a8744af13c7f7978a8399840f14a353b60cf21014647f71012982456d2b
+ languageName: node
+ linkType: hard
+
"acorn-jsx@npm:^5.3.2":
version: 5.3.2
resolution: "acorn-jsx@npm:5.3.2"
@@ -4942,6 +4984,24 @@ __metadata:
languageName: node
linkType: hard
+"acorn-walk@npm:^8.0.2":
+ version: 8.3.5
+ resolution: "acorn-walk@npm:8.3.5"
+ dependencies:
+ acorn: "npm:^8.11.0"
+ checksum: 10c0/e31bf5b5423ed1349437029d66d708b9fbd1b77a644b031501e2c753b028d13b56348210ed901d5b1d0d86eb3381c0a0fc0d0998511a9d546d1194936266a332
+ languageName: node
+ linkType: hard
+
+"acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.8.1":
+ version: 8.16.0
+ resolution: "acorn@npm:8.16.0"
+ bin:
+ acorn: bin/acorn
+ checksum: 10c0/c9c52697227661b68d0debaf972222d4f622aa06b185824164e153438afa7b08273432ca43ea792cadb24dada1d46f6f6bb1ef8de9956979288cc1b96bf9914e
+ languageName: node
+ linkType: hard
+
"acorn@npm:^8.14.0, acorn@npm:^8.8.2":
version: 8.14.1
resolution: "acorn@npm:8.14.1"
@@ -4958,6 +5018,15 @@ __metadata:
languageName: node
linkType: hard
+"agent-base@npm:6":
+ version: 6.0.2
+ resolution: "agent-base@npm:6.0.2"
+ dependencies:
+ debug: "npm:4"
+ checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261
+ languageName: node
+ linkType: hard
+
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
version: 7.1.3
resolution: "agent-base@npm:7.1.3"
@@ -5305,6 +5374,13 @@ __metadata:
languageName: node
linkType: hard
+"asynckit@npm:^0.4.0":
+ version: 0.4.0
+ resolution: "asynckit@npm:0.4.0"
+ checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d
+ languageName: node
+ linkType: hard
+
"atomically@npm:^2.0.3":
version: 2.0.3
resolution: "atomically@npm:2.0.3"
@@ -6024,6 +6100,15 @@ __metadata:
languageName: node
linkType: hard
+"combined-stream@npm:^1.0.8":
+ version: 1.0.8
+ resolution: "combined-stream@npm:1.0.8"
+ dependencies:
+ delayed-stream: "npm:~1.0.0"
+ checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5
+ languageName: node
+ linkType: hard
+
"command-exists@npm:^1.2.8":
version: 1.2.9
resolution: "command-exists@npm:1.2.9"
@@ -6450,6 +6535,29 @@ __metadata:
languageName: node
linkType: hard
+"cssom@npm:^0.5.0":
+ version: 0.5.0
+ resolution: "cssom@npm:0.5.0"
+ checksum: 10c0/8c4121c243baf0678c65dcac29b201ff0067dfecf978de9d5c83b2ff127a8fdefd2bfd54577f5ad8c80ed7d2c8b489ae01c82023545d010c4ecb87683fb403dd
+ languageName: node
+ linkType: hard
+
+"cssom@npm:~0.3.6":
+ version: 0.3.8
+ resolution: "cssom@npm:0.3.8"
+ checksum: 10c0/d74017b209440822f9e24d8782d6d2e808a8fdd58fa626a783337222fe1c87a518ba944d4c88499031b4786e68772c99dfae616638d71906fe9f203aeaf14411
+ languageName: node
+ linkType: hard
+
+"cssstyle@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "cssstyle@npm:2.3.0"
+ dependencies:
+ cssom: "npm:~0.3.6"
+ checksum: 10c0/863400da2a458f73272b9a55ba7ff05de40d850f22eb4f37311abebd7eff801cf1cd2fb04c4c92b8c3daed83fe766e52e4112afb7bc88d86c63a9c2256a7d178
+ languageName: node
+ linkType: hard
+
"csstype@npm:^3.0.2":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -6478,6 +6586,17 @@ __metadata:
languageName: node
linkType: hard
+"data-urls@npm:^3.0.2":
+ version: 3.0.2
+ resolution: "data-urls@npm:3.0.2"
+ dependencies:
+ abab: "npm:^2.0.6"
+ whatwg-mimetype: "npm:^3.0.0"
+ whatwg-url: "npm:^11.0.0"
+ checksum: 10c0/051c3aaaf3e961904f136aab095fcf6dff4db23a7fc759dd8ba7b3e6ba03fc07ef608086caad8ab910d864bd3b5e57d0d2f544725653d77c96a2c971567045f4
+ languageName: node
+ linkType: hard
+
"data-view-buffer@npm:^1.0.2":
version: 1.0.2
resolution: "data-view-buffer@npm:1.0.2"
@@ -6563,6 +6682,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js@npm:^10.4.2":
+ version: 10.6.0
+ resolution: "decimal.js@npm:10.6.0"
+ checksum: 10c0/07d69fbcc54167a340d2d97de95f546f9ff1f69d2b45a02fd7a5292412df3cd9eb7e23065e532a318f5474a2e1bccf8392fdf0443ef467f97f3bf8cb0477e5aa
+ languageName: node
+ linkType: hard
+
"dedent@npm:^0.7.0":
version: 0.7.0
resolution: "dedent@npm:0.7.0"
@@ -6714,6 +6840,13 @@ __metadata:
languageName: node
linkType: hard
+"delayed-stream@npm:~1.0.0":
+ version: 1.0.0
+ resolution: "delayed-stream@npm:1.0.0"
+ checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19
+ languageName: node
+ linkType: hard
+
"depd@npm:2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
@@ -6767,6 +6900,15 @@ __metadata:
languageName: node
linkType: hard
+"domexception@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "domexception@npm:4.0.0"
+ dependencies:
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 10c0/774277cd9d4df033f852196e3c0077a34dbd15a96baa4d166e0e47138a80f4c0bdf0d94e4703e6ff5883cec56bb821a6fff84402d8a498e31de7c87eb932a294
+ languageName: node
+ linkType: hard
+
"dot-prop@npm:^5.1.0":
version: 5.3.0
resolution: "dot-prop@npm:5.3.0"
@@ -6884,6 +7026,13 @@ __metadata:
languageName: node
linkType: hard
+"entities@npm:^6.0.0":
+ version: 6.0.1
+ resolution: "entities@npm:6.0.1"
+ checksum: 10c0/ed836ddac5acb34341094eb495185d527bd70e8632b6c0d59548cbfa23defdbae70b96f9a405c82904efa421230b5b3fd2283752447d737beffd3f3e6ee74414
+ languageName: node
+ linkType: hard
+
"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
@@ -7211,7 +7360,7 @@ __metadata:
languageName: node
linkType: hard
-"escodegen@npm:^2.1.0":
+"escodegen@npm:^2.0.0, escodegen@npm:^2.1.0":
version: 2.1.0
resolution: "escodegen@npm:2.1.0"
dependencies:
@@ -7932,6 +8081,19 @@ __metadata:
languageName: node
linkType: hard
+"form-data@npm:^4.0.0":
+ version: 4.0.5
+ resolution: "form-data@npm:4.0.5"
+ dependencies:
+ asynckit: "npm:^0.4.0"
+ combined-stream: "npm:^1.0.8"
+ es-set-tostringtag: "npm:^2.1.0"
+ hasown: "npm:^2.0.2"
+ mime-types: "npm:^2.1.12"
+ checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b
+ languageName: node
+ linkType: hard
+
"fresh@npm:0.5.2":
version: 0.5.2
resolution: "fresh@npm:0.5.2"
@@ -8544,6 +8706,15 @@ __metadata:
languageName: node
linkType: hard
+"html-encoding-sniffer@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "html-encoding-sniffer@npm:3.0.0"
+ dependencies:
+ whatwg-encoding: "npm:^2.0.0"
+ checksum: 10c0/b17b3b0fb5d061d8eb15121c3b0b536376c3e295ecaf09ba48dd69c6b6c957839db124fe1e2b3f11329753a4ee01aa7dedf63b7677999e86da17fbbdd82c5386
+ languageName: node
+ linkType: hard
+
"html-escaper@npm:^2.0.0":
version: 2.0.2
resolution: "html-escaper@npm:2.0.2"
@@ -8571,6 +8742,17 @@ __metadata:
languageName: node
linkType: hard
+"http-proxy-agent@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "http-proxy-agent@npm:5.0.0"
+ dependencies:
+ "@tootallnate/once": "npm:2"
+ agent-base: "npm:6"
+ debug: "npm:4"
+ checksum: 10c0/32a05e413430b2c1e542e5c74b38a9f14865301dd69dff2e53ddb684989440e3d2ce0c4b64d25eb63cf6283e6265ff979a61cf93e3ca3d23047ddfdc8df34a32
+ languageName: node
+ linkType: hard
+
"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1":
version: 7.0.2
resolution: "http-proxy-agent@npm:7.0.2"
@@ -8581,6 +8763,16 @@ __metadata:
languageName: node
linkType: hard
+"https-proxy-agent@npm:^5.0.1":
+ version: 5.0.1
+ resolution: "https-proxy-agent@npm:5.0.1"
+ dependencies:
+ agent-base: "npm:6"
+ debug: "npm:4"
+ checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1
+ languageName: node
+ linkType: hard
+
"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6":
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
@@ -8621,7 +8813,7 @@ __metadata:
languageName: node
linkType: hard
-"iconv-lite@npm:^0.6.2":
+"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
dependencies:
@@ -9148,6 +9340,13 @@ __metadata:
languageName: node
linkType: hard
+"is-potential-custom-element-name@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "is-potential-custom-element-name@npm:1.0.1"
+ checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9
+ languageName: node
+ linkType: hard
+
"is-regex@npm:^1.2.1":
version: 1.2.1
resolution: "is-regex@npm:1.2.1"
@@ -9598,6 +9797,27 @@ __metadata:
languageName: node
linkType: hard
+"jest-environment-jsdom@npm:^29.7.0":
+ version: 29.7.0
+ resolution: "jest-environment-jsdom@npm:29.7.0"
+ dependencies:
+ "@jest/environment": "npm:^29.7.0"
+ "@jest/fake-timers": "npm:^29.7.0"
+ "@jest/types": "npm:^29.6.3"
+ "@types/jsdom": "npm:^20.0.0"
+ "@types/node": "npm:*"
+ jest-mock: "npm:^29.7.0"
+ jest-util: "npm:^29.7.0"
+ jsdom: "npm:^20.0.0"
+ peerDependencies:
+ canvas: ^2.5.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+ checksum: 10c0/139b94e2c8ec1bb5a46ce17df5211da65ce867354b3fd4e00fa6a0d1da95902df4cf7881273fc6ea937e5c325d39d6773f0d41b6c469363334de9d489d2c321f
+ languageName: node
+ linkType: hard
+
"jest-environment-node@npm:^29.7.0":
version: 29.7.0
resolution: "jest-environment-node@npm:29.7.0"
@@ -9966,6 +10186,45 @@ __metadata:
languageName: node
linkType: hard
+"jsdom@npm:^20.0.0":
+ version: 20.0.3
+ resolution: "jsdom@npm:20.0.3"
+ dependencies:
+ abab: "npm:^2.0.6"
+ acorn: "npm:^8.8.1"
+ acorn-globals: "npm:^7.0.0"
+ cssom: "npm:^0.5.0"
+ cssstyle: "npm:^2.3.0"
+ data-urls: "npm:^3.0.2"
+ decimal.js: "npm:^10.4.2"
+ domexception: "npm:^4.0.0"
+ escodegen: "npm:^2.0.0"
+ form-data: "npm:^4.0.0"
+ html-encoding-sniffer: "npm:^3.0.0"
+ http-proxy-agent: "npm:^5.0.0"
+ https-proxy-agent: "npm:^5.0.1"
+ is-potential-custom-element-name: "npm:^1.0.1"
+ nwsapi: "npm:^2.2.2"
+ parse5: "npm:^7.1.1"
+ saxes: "npm:^6.0.0"
+ symbol-tree: "npm:^3.2.4"
+ tough-cookie: "npm:^4.1.2"
+ w3c-xmlserializer: "npm:^4.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ whatwg-encoding: "npm:^2.0.0"
+ whatwg-mimetype: "npm:^3.0.0"
+ whatwg-url: "npm:^11.0.0"
+ ws: "npm:^8.11.0"
+ xml-name-validator: "npm:^4.0.0"
+ peerDependencies:
+ canvas: ^2.5.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+ checksum: 10c0/b109073bb826a966db7828f46cb1d7371abecd30f182b143c52be5fe1ed84513bbbe995eb3d157241681fcd18331381e61e3dc004d4949f3a63bca02f6214902
+ languageName: node
+ linkType: hard
+
"jsesc@npm:^3.0.2":
version: 3.1.0
resolution: "jsesc@npm:3.1.0"
@@ -11030,7 +11289,7 @@ __metadata:
languageName: node
linkType: hard
-"mime-types@npm:2.1.35, mime-types@npm:^2.1.27, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
+"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
@@ -11419,6 +11678,13 @@ __metadata:
languageName: node
linkType: hard
+"nwsapi@npm:^2.2.2":
+ version: 2.2.24
+ resolution: "nwsapi@npm:2.2.24"
+ checksum: 10c0/9bc04ee9c7698f1b5506778d36f7382962f71667205d441d6a50f6180ee92328e770b76be78b907817ee103241b29984d3a17ae387e4723aebe0aeaed7a7c3a1
+ languageName: node
+ linkType: hard
+
"ob1@npm:0.83.1":
version: 0.83.1
resolution: "ob1@npm:0.83.1"
@@ -11874,6 +12140,15 @@ __metadata:
languageName: node
linkType: hard
+"parse5@npm:^7.0.0, parse5@npm:^7.1.1":
+ version: 7.3.0
+ resolution: "parse5@npm:7.3.0"
+ dependencies:
+ entities: "npm:^6.0.0"
+ checksum: 10c0/7fd2e4e247e85241d6f2a464d0085eed599a26d7b0a5233790c49f53473232eb85350e8133344d9b3fd58b89339e7ad7270fe1f89d28abe50674ec97b87f80b5
+ languageName: node
+ linkType: hard
+
"parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
@@ -12347,6 +12622,15 @@ __metadata:
languageName: node
linkType: hard
+"psl@npm:^1.1.33":
+ version: 1.15.0
+ resolution: "psl@npm:1.15.0"
+ dependencies:
+ punycode: "npm:^2.3.1"
+ checksum: 10c0/d8d45a99e4ca62ca12ac3c373e63d80d2368d38892daa40cfddaa1eb908be98cd549ac059783ef3a56cfd96d57ae8e2fd9ae53d1378d90d42bc661ff924e102a
+ languageName: node
+ linkType: hard
+
"pump@npm:^3.0.0":
version: 3.0.2
resolution: "pump@npm:3.0.2"
@@ -12364,7 +12648,7 @@ __metadata:
languageName: node
linkType: hard
-"punycode@npm:^2.1.0":
+"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
@@ -12396,6 +12680,13 @@ __metadata:
languageName: node
linkType: hard
+"querystringify@npm:^2.1.1":
+ version: 2.2.0
+ resolution: "querystringify@npm:2.2.0"
+ checksum: 10c0/3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa
+ languageName: node
+ linkType: hard
+
"queue-microtask@npm:^1.2.2":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@@ -12594,6 +12885,7 @@ __metadata:
eslint-config-prettier: "npm:^10.1.1"
eslint-plugin-prettier: "npm:^5.2.3"
jest: "npm:^29.7.0"
+ jest-environment-jsdom: "npm:^29.7.0"
playwright: "npm:1.58.2"
prettier: "npm:^3.0.3"
react: "npm:19.1.0"
@@ -13032,6 +13324,13 @@ __metadata:
languageName: node
linkType: hard
+"requires-port@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "requires-port@npm:1.0.0"
+ checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267
+ languageName: node
+ linkType: hard
+
"resolve-cwd@npm:^3.0.0":
version: 3.0.0
resolution: "resolve-cwd@npm:3.0.0"
@@ -13341,6 +13640,15 @@ __metadata:
languageName: node
linkType: hard
+"saxes@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "saxes@npm:6.0.0"
+ dependencies:
+ xmlchars: "npm:^2.2.0"
+ checksum: 10c0/3847b839f060ef3476eb8623d099aa502ad658f5c40fd60c105ebce86d244389b0d76fcae30f4d0c728d7705ceb2f7e9b34bb54717b6a7dbedaf5dad2d9a4b74
+ languageName: node
+ linkType: hard
+
"scheduler@npm:0.26.0, scheduler@npm:^0.26.0":
version: 0.26.0
resolution: "scheduler@npm:0.26.0"
@@ -14034,6 +14342,13 @@ __metadata:
languageName: node
linkType: hard
+"symbol-tree@npm:^3.2.4":
+ version: 3.2.4
+ resolution: "symbol-tree@npm:3.2.4"
+ checksum: 10c0/dfbe201ae09ac6053d163578778c53aa860a784147ecf95705de0cd23f42c851e1be7889241495e95c37cabb058edb1052f141387bef68f705afc8f9dd358509
+ languageName: node
+ linkType: hard
+
"synckit@npm:^0.11.0":
version: 0.11.4
resolution: "synckit@npm:0.11.4"
@@ -14163,6 +14478,27 @@ __metadata:
languageName: node
linkType: hard
+"tough-cookie@npm:^4.1.2":
+ version: 4.1.4
+ resolution: "tough-cookie@npm:4.1.4"
+ dependencies:
+ psl: "npm:^1.1.33"
+ punycode: "npm:^2.1.1"
+ universalify: "npm:^0.2.0"
+ url-parse: "npm:^1.5.3"
+ checksum: 10c0/aca7ff96054f367d53d1e813e62ceb7dd2eda25d7752058a74d64b7266fd07be75908f3753a32ccf866a2f997604b414cfb1916d6e7f69bc64d9d9939b0d6c45
+ languageName: node
+ linkType: hard
+
+"tr46@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "tr46@npm:3.0.0"
+ dependencies:
+ punycode: "npm:^2.1.1"
+ checksum: 10c0/cdc47cad3a9d0b6cb293e39ccb1066695ae6fdd39b9e4f351b010835a1f8b4f3a6dc3a55e896b421371187f22b48d7dac1b693de4f6551bdef7b6ab6735dfe3b
+ languageName: node
+ linkType: hard
+
"trim-newlines@npm:^4.0.2":
version: 4.1.1
resolution: "trim-newlines@npm:4.1.1"
@@ -14577,6 +14913,13 @@ __metadata:
languageName: node
linkType: hard
+"universalify@npm:^0.2.0":
+ version: 0.2.0
+ resolution: "universalify@npm:0.2.0"
+ checksum: 10c0/cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe
+ languageName: node
+ linkType: hard
+
"universalify@npm:^2.0.0":
version: 2.0.1
resolution: "universalify@npm:2.0.1"
@@ -14639,6 +14982,16 @@ __metadata:
languageName: node
linkType: hard
+"url-parse@npm:^1.5.3":
+ version: 1.5.10
+ resolution: "url-parse@npm:1.5.10"
+ dependencies:
+ querystringify: "npm:^2.1.1"
+ requires-port: "npm:^1.0.0"
+ checksum: 10c0/bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87
+ languageName: node
+ linkType: hard
+
"use-sync-external-store@npm:^1.4.0":
version: 1.6.0
resolution: "use-sync-external-store@npm:1.6.0"
@@ -14759,6 +15112,15 @@ __metadata:
languageName: node
linkType: hard
+"w3c-xmlserializer@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "w3c-xmlserializer@npm:4.0.0"
+ dependencies:
+ xml-name-validator: "npm:^4.0.0"
+ checksum: 10c0/02cc66d6efc590bd630086cd88252444120f5feec5c4043932b0d0f74f8b060512f79dc77eb093a7ad04b4f02f39da79ce4af47ceb600f2bf9eacdc83204b1a8
+ languageName: node
+ linkType: hard
+
"walker@npm:^1.0.7, walker@npm:^1.0.8":
version: 1.0.8
resolution: "walker@npm:1.0.8"
@@ -14777,6 +15139,22 @@ __metadata:
languageName: node
linkType: hard
+"webidl-conversions@npm:^7.0.0":
+ version: 7.0.0
+ resolution: "webidl-conversions@npm:7.0.0"
+ checksum: 10c0/228d8cb6d270c23b0720cb2d95c579202db3aaf8f633b4e9dd94ec2000a04e7e6e43b76a94509cdb30479bd00ae253ab2371a2da9f81446cc313f89a4213a2c4
+ languageName: node
+ linkType: hard
+
+"whatwg-encoding@npm:^2.0.0":
+ version: 2.0.0
+ resolution: "whatwg-encoding@npm:2.0.0"
+ dependencies:
+ iconv-lite: "npm:0.6.3"
+ checksum: 10c0/91b90a49f312dc751496fd23a7e68981e62f33afe938b97281ad766235c4872fc4e66319f925c5e9001502b3040dd25a33b02a9c693b73a4cbbfdc4ad10c3e3e
+ languageName: node
+ linkType: hard
+
"whatwg-fetch@npm:^3.0.0":
version: 3.6.20
resolution: "whatwg-fetch@npm:3.6.20"
@@ -14784,6 +15162,23 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-mimetype@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "whatwg-mimetype@npm:3.0.0"
+ checksum: 10c0/323895a1cda29a5fb0b9ca82831d2c316309fede0365047c4c323073e3239067a304a09a1f4b123b9532641ab604203f33a1403b5ca6a62ef405bcd7a204080f
+ languageName: node
+ linkType: hard
+
+"whatwg-url@npm:^11.0.0":
+ version: 11.0.0
+ resolution: "whatwg-url@npm:11.0.0"
+ dependencies:
+ tr46: "npm:^3.0.0"
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 10c0/f7ec264976d7c725e0696fcaf9ebe056e14422eacbf92fdbb4462034609cba7d0c85ffa1aab05e9309d42969bcf04632ba5ed3f3882c516d7b093053315bf4c1
+ languageName: node
+ linkType: hard
+
"when-exit@npm:^2.1.1":
version: 2.1.4
resolution: "when-exit@npm:2.1.4"
@@ -15005,6 +15400,21 @@ __metadata:
languageName: node
linkType: hard
+"ws@npm:^8.11.0":
+ version: 8.21.0
+ resolution: "ws@npm:8.21.0"
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ">=5.0.2"
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ checksum: 10c0/ef4a243476283fc49bc7550966c4af4aa0eef56273837211e700de3b664e08604a760cdddcb5ba43c049140e74ccfec5b0ee0bb439e08c2adf9138902fdde5f9
+ languageName: node
+ linkType: hard
+
"xdg-basedir@npm:^5.1.0":
version: 5.1.0
resolution: "xdg-basedir@npm:5.1.0"
@@ -15012,6 +15422,20 @@ __metadata:
languageName: node
linkType: hard
+"xml-name-validator@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "xml-name-validator@npm:4.0.0"
+ checksum: 10c0/c1bfa219d64e56fee265b2bd31b2fcecefc063ee802da1e73bad1f21d7afd89b943c9e2c97af2942f60b1ad46f915a4c81e00039c7d398b53cf410e29d3c30bd
+ languageName: node
+ linkType: hard
+
+"xmlchars@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "xmlchars@npm:2.2.0"
+ checksum: 10c0/b64b535861a6f310c5d9bfa10834cf49127c71922c297da9d4d1b45eeaae40bf9b4363275876088fbe2667e5db028d2cd4f8ee72eed9bede840a67d57dab7593
+ languageName: node
+ linkType: hard
+
"y18n@npm:^4.0.0":
version: 4.0.3
resolution: "y18n@npm:4.0.3"