From 9bfb8976410ad68d78be9c37beaca123383133c5 Mon Sep 17 00:00:00 2001
From: Carsten Milling
Date: Sat, 11 Jul 2026 11:42:59 +0200
Subject: [PATCH 01/13] Add TypeScript and Vitest scaffolding
Green baseline for the v3.0.0 migration: introduce TS + Vitest without
touching any runtime code. Converts vite.config and eslint.config to TS,
adds tsconfig.json, wires up jsdom-based Vitest with jest-dom matchers.
Fixes two pre-existing lint errors surfaced by the updated globals
package.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
eslint.config.mjs => eslint.config.ts | 18 +-
package.json | 18 +-
pnpm-lock.yaml | 1146 ++++++++++++++++++++++++-
src/components/EzlinavisComponent.jsx | 2 +-
src/components/Info.jsx | 4 +-
src/test-setup.test.ts | 7 +
src/test-setup.ts | 1 +
tsconfig.json | 23 +
vite.config.mjs | 14 -
vite.config.ts | 20 +
10 files changed, 1208 insertions(+), 45 deletions(-)
rename eslint.config.mjs => eslint.config.ts (60%)
create mode 100644 src/test-setup.test.ts
create mode 100644 src/test-setup.ts
create mode 100644 tsconfig.json
delete mode 100644 vite.config.mjs
create mode 100644 vite.config.ts
diff --git a/eslint.config.mjs b/eslint.config.ts
similarity index 60%
rename from eslint.config.mjs
rename to eslint.config.ts
index 9389886..0653ea6 100644
--- a/eslint.config.mjs
+++ b/eslint.config.ts
@@ -2,11 +2,19 @@ import globals from 'globals';
import pluginJs from '@eslint/js';
import pluginReact from 'eslint-plugin-react';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
+import type {Linter} from 'eslint';
-export default [
- { files: ['**/*.{js,mjs,cjs,jsx}'] },
- { languageOptions: { globals: globals.browser } },
- { ignores: ['src/grammar.js', 'grammar.js', 'src/App.test.jsx'] },
+const config: Linter.Config[] = [
+ {files: ['**/*.{js,mjs,cjs,jsx}']},
+ {languageOptions: {globals: globals.browser}},
+ {
+ ignores: [
+ 'src/grammar.js',
+ 'grammar.js',
+ 'src/App.test.jsx',
+ 'build/**',
+ ],
+ },
pluginJs.configs.recommended,
pluginReact.configs.flat['jsx-runtime'],
eslintPluginPrettierRecommended,
@@ -19,3 +27,5 @@ export default [
},
},
];
+
+export default config;
diff --git a/package.json b/package.json
index 2ce493c..b30b06a 100644
--- a/package.json
+++ b/package.json
@@ -21,13 +21,25 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@types/eslint": "^9.6.1",
+ "@types/node": "^26.1.1",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3",
+ "@vitest/ui": "^4.1.10",
"eslint": "^10.6.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-react": "^7.37.4",
+ "globals": "^17.7.0",
+ "jiti": "^2.7.0",
+ "jsdom": "^29.1.1",
"prettier": "^3.9.5",
- "vite": "^8.1.4"
+ "typescript": "^7.0.2",
+ "vite": "^8.1.4",
+ "vitest": "^4.1.10"
},
"scripts": {
"grammar": "nearleyc src/grammar.ne -o src/grammar.js",
@@ -35,6 +47,10 @@
"build": "pnpm run grammar && vite build",
"start": "pnpm run dev",
"lint": "eslint src",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:ui": "vitest --ui",
"preview": "pnpm run grammar && vite preview"
},
"browserslist": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e1872d7..9d9b4da 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,35 +35,137 @@ importers:
devDependencies:
'@eslint/js':
specifier: ^10.0.1
- version: 10.0.1(eslint@10.6.0)
+ version: 10.0.1(eslint@10.6.0(jiti@2.7.0))
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
+ '@types/eslint':
+ specifier: ^9.6.1
+ version: 9.6.1
+ '@types/node':
+ specifier: ^26.1.1
+ version: 26.1.1
+ '@types/react':
+ specifier: ^19.2.17
+ version: 19.2.17
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.17)
'@vitejs/plugin-react':
specifier: ^6.0.3
- version: 6.0.3(vite@8.1.4(esbuild@0.27.7))
+ version: 6.0.3(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))
+ '@vitest/ui':
+ specifier: ^4.1.10
+ version: 4.1.10(vitest@4.1.10)
eslint:
specifier: ^10.6.0
- version: 10.6.0
+ version: 10.6.0(jiti@2.7.0)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@10.6.0)
+ version: 10.1.8(eslint@10.6.0(jiti@2.7.0))
eslint-plugin-prettier:
specifier: ^5.5.6
- version: 5.5.6(eslint-config-prettier@10.1.8(eslint@10.6.0))(eslint@10.6.0)(prettier@3.9.5)
+ version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)))(eslint@10.6.0(jiti@2.7.0))(prettier@3.9.5)
eslint-plugin-react:
specifier: ^7.37.4
- version: 7.37.5(eslint@10.6.0)
+ version: 7.37.5(eslint@10.6.0(jiti@2.7.0))
+ globals:
+ specifier: ^17.7.0
+ version: 17.7.0
+ jiti:
+ specifier: ^2.7.0
+ version: 2.7.0
+ jsdom:
+ specifier: ^29.1.1
+ version: 29.1.1
prettier:
specifier: ^3.9.5
version: 3.9.5
+ typescript:
+ specifier: ^7.0.2
+ version: 7.0.2
vite:
specifier: ^8.1.4
- version: 8.1.4(esbuild@0.27.7)
+ version: 8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)
+ vitest:
+ specifier: ^4.1.10
+ version: 4.1.10(@types/node@26.1.1)(@vitest/ui@4.1.10)(jsdom@29.1.1)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))
packages:
+ '@adobe/css-tools@4.5.0':
+ resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==}
+
+ '@asamuzakjp/css-color@5.1.11':
+ resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/generational-cache@1.0.1':
+ resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@babel/code-frame@7.29.7':
+ resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/runtime@7.27.0':
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'}
+ '@bramus/specificity@2.4.2':
+ resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+ hasBin: true
+
+ '@csstools/color-helpers@6.1.0':
+ resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.2.1':
+ resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.1.9':
+ resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.6':
+ resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
'@emnapi/core@1.11.1':
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
@@ -268,6 +370,15 @@ packages:
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ '@exodus/bytes@1.15.1':
+ resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
'@humanfs/core@0.19.2':
resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
engines: {node: '>=18.18.0'}
@@ -288,6 +399,9 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
'@napi-rs/wasm-runtime@1.1.6':
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
peerDependencies:
@@ -301,6 +415,9 @@ packages:
resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==}
engines: {node: ^14.18.0 || >=16.0.0}
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
'@rolldown/binding-android-arm64@1.1.5':
resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -393,9 +510,47 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.2':
+ resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@tybys/wasm-util@0.10.3':
resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/eslint@9.6.1':
+ resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
+
'@types/esrecurse@4.3.1':
resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
@@ -405,6 +560,137 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/node@26.1.1':
+ resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.17':
+ resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
+
+ '@typescript/typescript-aix-ppc64@7.0.2':
+ resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@typescript/typescript-darwin-arm64@7.0.2':
+ resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@typescript/typescript-darwin-x64@7.0.2':
+ resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@typescript/typescript-freebsd-arm64@7.0.2':
+ resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@typescript/typescript-freebsd-x64@7.0.2':
+ resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@typescript/typescript-linux-arm64@7.0.2':
+ resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@typescript/typescript-linux-arm@7.0.2':
+ resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@typescript/typescript-linux-loong64@7.0.2':
+ resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@typescript/typescript-linux-mips64el@7.0.2':
+ resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@typescript/typescript-linux-ppc64@7.0.2':
+ resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@typescript/typescript-linux-riscv64@7.0.2':
+ resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@typescript/typescript-linux-s390x@7.0.2':
+ resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==}
+ engines: {node: '>=16.20.0'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@typescript/typescript-linux-x64@7.0.2':
+ resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@typescript/typescript-netbsd-arm64@7.0.2':
+ resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@typescript/typescript-netbsd-x64@7.0.2':
+ resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@typescript/typescript-openbsd-arm64@7.0.2':
+ resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@typescript/typescript-openbsd-x64@7.0.2':
+ resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@typescript/typescript-sunos-x64@7.0.2':
+ resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@typescript/typescript-win32-arm64@7.0.2':
+ resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==}
+ engines: {node: '>=16.20.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@typescript/typescript-win32-x64@7.0.2':
+ resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==}
+ engines: {node: '>=16.20.0'}
+ cpu: [x64]
+ os: [win32]
+
'@vitejs/plugin-react@6.0.3':
resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -418,6 +704,40 @@ packages:
babel-plugin-react-compiler:
optional: true
+ '@vitest/expect@4.1.10':
+ resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
+
+ '@vitest/mocker@4.1.10':
+ resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.1.10':
+ resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
+
+ '@vitest/runner@4.1.10':
+ resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
+
+ '@vitest/snapshot@4.1.10':
+ resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
+
+ '@vitest/spy@4.1.10':
+ resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
+
+ '@vitest/ui@4.1.10':
+ resolution: {integrity: sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==}
+ peerDependencies:
+ vitest: 4.1.10
+
+ '@vitest/utils@4.1.10':
+ resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -431,6 +751,21 @@ packages:
ajv@6.15.0:
resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
array-buffer-byte-length@1.0.2:
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
@@ -459,6 +794,10 @@ packages:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
async-function@1.0.0:
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
engines: {node: '>= 0.4'}
@@ -477,6 +816,9 @@ packages:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@@ -496,6 +838,10 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
+ chai@6.2.2:
+ resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ engines: {node: '>=18'}
+
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
@@ -505,6 +851,9 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
core-js@2.6.12:
resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==}
deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
@@ -513,6 +862,20 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ data-urls@7.0.0:
+ resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
data-view-buffer@1.0.2:
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
@@ -534,6 +897,9 @@ packages:
supports-color:
optional: true
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -545,6 +911,10 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
@@ -556,6 +926,12 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dom-helpers@3.4.0:
resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==}
@@ -563,6 +939,10 @@ packages:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
es-abstract@1.23.9:
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
engines: {node: '>= 0.4'}
@@ -579,6 +959,9 @@ packages:
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
engines: {node: '>= 0.4'}
+ es-module-lexer@2.3.0:
+ resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==}
+
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -668,10 +1051,17 @@ packages:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ expect-type@1.4.0:
+ resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
+ engines: {node: '>=12.0.0'}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -693,6 +1083,9 @@ packages:
picomatch:
optional: true
+ fflate@0.8.3:
+ resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==}
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -743,6 +1136,10 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
+ globals@17.7.0:
+ resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==}
+ engines: {node: '>=18'}
+
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
@@ -774,6 +1171,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -782,6 +1183,10 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
@@ -845,6 +1250,9 @@ packages:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@@ -891,9 +1299,22 @@ packages:
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'}
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ jsdom@29.1.1:
+ resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -998,10 +1419,28 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
+ lru-cache@11.5.2:
+ resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
+ engines: {node: 20 || >=22}
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
@@ -1012,6 +1451,10 @@ packages:
moo@0.5.2:
resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
+ mrmime@2.0.1:
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
+ engines: {node: '>=10'}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -1058,6 +1501,10 @@ packages:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
+ obug@2.1.3:
+ resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==}
+ engines: {node: '>=12.20.0'}
+
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -1074,6 +1521,9 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+ parse5@8.0.1:
+ resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -1085,6 +1535,9 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -1113,6 +1566,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
prop-types-extra@1.1.1:
resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==}
peerDependencies:
@@ -1151,6 +1608,9 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react-overlays@0.7.4:
resolution: {integrity: sha512-7vsooMx3siLAuEfTs8FYeP/lAORWWFXTO8PON3KgX0Htq1Oa+po6ioSjGyO0/GO5CVSMNhpWt6V2opeexHgBuQ==}
peerDependencies:
@@ -1168,6 +1628,10 @@ packages:
resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
engines: {node: '>=0.10.0'}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -1182,6 +1646,10 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
resolve@2.0.0-next.5:
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true
@@ -1207,6 +1675,10 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.19.1:
resolution: {integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==}
@@ -1250,10 +1722,23 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@4.2.0:
+ resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
+
string.prototype.matchall@4.0.12:
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
engines: {node: '>= 0.4'}
@@ -1273,18 +1758,55 @@ packages:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
engines: {node: '>= 0.4'}
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
synckit@0.11.13:
resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
engines: {node: ^14.18.0 || >=16.0.0}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@1.2.4:
+ resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
+ engines: {node: '>=18'}
+
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
+ tinyrainbow@3.1.0:
+ resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@7.4.8:
+ resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==}
+
+ tldts@7.4.8:
+ resolution: {integrity: sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==}
+ hasBin: true
+
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
+ tough-cookie@6.0.2:
+ resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -1308,6 +1830,11 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
+ typescript@7.0.2:
+ resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==}
+ engines: {node: '>=16.20.0'}
+ hasBin: true
+
unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
@@ -1317,6 +1844,13 @@ packages:
peerDependencies:
react: '>=0.11.0'
+ undici-types@8.3.0:
+ resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
+
+ undici@7.28.0:
+ resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
+ engines: {node: '>=20.18.1'}
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -1363,12 +1897,69 @@ packages:
yaml:
optional: true
+ vitest@4.1.10:
+ resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@opentelemetry/api': ^1.9.0
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.1.10
+ '@vitest/browser-preview': 4.1.10
+ '@vitest/browser-webdriverio': 4.1.10
+ '@vitest/coverage-istanbul': 4.1.10
+ '@vitest/coverage-v8': 4.1.10
+ '@vitest/ui': 4.1.10
+ happy-dom: '*'
+ jsdom: '*'
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/coverage-istanbul':
+ optional: true
+ '@vitest/coverage-v8':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
warning@3.0.0:
resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==}
warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@16.0.1:
+ resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -1390,20 +1981,90 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
snapshots:
+ '@adobe/css-tools@4.5.0': {}
+
+ '@asamuzakjp/css-color@5.1.11':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+
+ '@asamuzakjp/generational-cache@1.0.1': {}
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@babel/code-frame@7.29.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.29.7
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
'@babel/runtime@7.27.0':
dependencies:
regenerator-runtime: 0.14.1
+ '@bramus/specificity@2.4.2':
+ dependencies:
+ css-tree: 3.2.1
+
+ '@csstools/color-helpers@6.1.0': {}
+
+ '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.1.0
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
'@emnapi/core@1.11.1':
dependencies:
'@emnapi/wasi-threads': 1.2.2
@@ -1498,9 +2159,9 @@ snapshots:
'@esbuild/win32-x64@0.27.7':
optional: true
- '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)':
+ '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))':
dependencies:
- eslint: 10.6.0
+ eslint: 10.6.0(jiti@2.7.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -1521,9 +2182,9 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/js@10.0.1(eslint@10.6.0)':
+ '@eslint/js@10.0.1(eslint@10.6.0(jiti@2.7.0))':
optionalDependencies:
- eslint: 10.6.0
+ eslint: 10.6.0(jiti@2.7.0)
'@eslint/object-schema@3.0.5': {}
@@ -1532,6 +2193,8 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
+ '@exodus/bytes@1.15.1': {}
+
'@humanfs/core@0.19.2':
dependencies:
'@humanfs/types': 0.15.0
@@ -1548,6 +2211,8 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
dependencies:
'@emnapi/core': 1.11.1
@@ -1559,6 +2224,8 @@ snapshots:
'@pkgr/core@0.3.6': {}
+ '@polka/url@1.0.0-next.29': {}
+
'@rolldown/binding-android-arm64@1.1.5':
optional: true
@@ -1610,21 +2277,191 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
+ '@standard-schema/spec@1.1.0': {}
+
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/runtime': 7.27.0
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.5.0
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@16.14.0(react@16.14.0))(react@16.14.0)':
+ dependencies:
+ '@babel/runtime': 7.27.0
+ '@testing-library/dom': 10.4.1
+ react: 16.14.0
+ react-dom: 16.14.0(react@16.14.0)
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
'@tybys/wasm-util@0.10.3':
dependencies:
tslib: 2.8.1
optional: true
+ '@types/aria-query@5.0.4': {}
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/eslint@9.6.1':
+ dependencies:
+ '@types/estree': 1.0.9
+ '@types/json-schema': 7.0.15
+
'@types/esrecurse@4.3.1': {}
'@types/estree@1.0.9': {}
'@types/json-schema@7.0.15': {}
- '@vitejs/plugin-react@6.0.3(vite@8.1.4(esbuild@0.27.7))':
+ '@types/node@26.1.1':
+ dependencies:
+ undici-types: 8.3.0
+
+ '@types/react-dom@19.2.3(@types/react@19.2.17)':
+ dependencies:
+ '@types/react': 19.2.17
+
+ '@types/react@19.2.17':
+ dependencies:
+ csstype: 3.2.3
+
+ '@typescript/typescript-aix-ppc64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-darwin-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-darwin-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-freebsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-freebsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-arm@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-loong64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-mips64el@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-ppc64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-riscv64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-s390x@7.0.2':
+ optional: true
+
+ '@typescript/typescript-linux-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-netbsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-netbsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-openbsd-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-openbsd-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-sunos-x64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-win32-arm64@7.0.2':
+ optional: true
+
+ '@typescript/typescript-win32-x64@7.0.2':
+ optional: true
+
+ '@vitejs/plugin-react@6.0.3(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.1.4(esbuild@0.27.7)
+ vite: 8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)
+
+ '@vitest/expect@4.1.10':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.1.10
+ '@vitest/utils': 4.1.10
+ chai: 6.2.2
+ tinyrainbow: 3.1.0
+
+ '@vitest/mocker@4.1.10(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))':
+ dependencies:
+ '@vitest/spy': 4.1.10
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)
+
+ '@vitest/pretty-format@4.1.10':
+ dependencies:
+ tinyrainbow: 3.1.0
+
+ '@vitest/runner@4.1.10':
+ dependencies:
+ '@vitest/utils': 4.1.10
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.1.10':
+ dependencies:
+ '@vitest/pretty-format': 4.1.10
+ '@vitest/utils': 4.1.10
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@4.1.10': {}
+
+ '@vitest/ui@4.1.10(vitest@4.1.10)':
+ dependencies:
+ '@vitest/utils': 4.1.10
+ fflate: 0.8.3
+ flatted: 3.4.2
+ pathe: 2.0.3
+ sirv: 3.0.2
+ tinyglobby: 0.2.17
+ tinyrainbow: 3.1.0
+ vitest: 4.1.10(@types/node@26.1.1)(@vitest/ui@4.1.10)(jsdom@29.1.1)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))
+
+ '@vitest/utils@4.1.10':
+ dependencies:
+ '@vitest/pretty-format': 4.1.10
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.1.0
acorn-jsx@5.3.2(acorn@8.17.0):
dependencies:
@@ -1639,6 +2476,16 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@5.2.0: {}
+
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ aria-query@5.3.2: {}
+
array-buffer-byte-length@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -1694,6 +2541,8 @@ snapshots:
get-intrinsic: 1.3.0
is-array-buffer: 3.0.5
+ assertion-error@2.0.1: {}
+
async-function@1.0.0: {}
available-typed-arrays@1.0.7:
@@ -1709,6 +2558,10 @@ snapshots:
balanced-match@4.0.4: {}
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@@ -1735,12 +2588,16 @@ snapshots:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
+ chai@6.2.2: {}
+
classnames@2.5.1: {}
commander@2.20.3: {}
concat-map@0.0.1: {}
+ convert-source-map@2.0.0: {}
+
core-js@2.6.12: {}
cross-spawn@7.0.6:
@@ -1749,6 +2606,22 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
+ css.escape@1.5.1: {}
+
+ csstype@3.2.3: {}
+
+ data-urls@7.0.0:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
data-view-buffer@1.0.2:
dependencies:
call-bound: 1.0.4
@@ -1771,6 +2644,8 @@ snapshots:
dependencies:
ms: 2.1.3
+ decimal.js@10.6.0: {}
+
deep-is@0.1.4: {}
define-data-property@1.1.4:
@@ -1785,6 +2660,8 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
+ dequal@2.0.3: {}
+
detect-libc@2.1.2: {}
discontinuous-range@1.0.0: {}
@@ -1793,6 +2670,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dom-helpers@3.4.0:
dependencies:
'@babel/runtime': 7.27.0
@@ -1803,6 +2684,8 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
+ entities@8.0.0: {}
+
es-abstract@1.23.9:
dependencies:
array-buffer-byte-length: 1.0.2
@@ -1880,6 +2763,8 @@ snapshots:
iterator.prototype: 1.1.5
safe-array-concat: 1.1.3
+ es-module-lexer@2.3.0: {}
+
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -1933,20 +2818,21 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-prettier@10.1.8(eslint@10.6.0):
+ eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)):
dependencies:
- eslint: 10.6.0
+ eslint: 10.6.0(jiti@2.7.0)
- eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@10.6.0))(eslint@10.6.0)(prettier@3.9.5):
+ eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)))(eslint@10.6.0(jiti@2.7.0))(prettier@3.9.5):
dependencies:
- eslint: 10.6.0
+ eslint: 10.6.0(jiti@2.7.0)
prettier: 3.9.5
prettier-linter-helpers: 1.0.1
synckit: 0.11.13
optionalDependencies:
- eslint-config-prettier: 10.1.8(eslint@10.6.0)
+ '@types/eslint': 9.6.1
+ eslint-config-prettier: 10.1.8(eslint@10.6.0(jiti@2.7.0))
- eslint-plugin-react@7.37.5(eslint@10.6.0):
+ eslint-plugin-react@7.37.5(eslint@10.6.0(jiti@2.7.0)):
dependencies:
array-includes: 3.1.8
array.prototype.findlast: 1.2.5
@@ -1954,7 +2840,7 @@ snapshots:
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
es-iterator-helpers: 1.2.1
- eslint: 10.6.0
+ eslint: 10.6.0(jiti@2.7.0)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
@@ -1979,9 +2865,9 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
- eslint@10.6.0:
+ eslint@10.6.0(jiti@2.7.0):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5
'@eslint/config-helpers': 0.6.0
@@ -2011,6 +2897,8 @@ snapshots:
minimatch: 10.2.5
natural-compare: 1.4.0
optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
transitivePeerDependencies:
- supports-color
@@ -2030,8 +2918,14 @@ snapshots:
estraverse@5.3.0: {}
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+
esutils@2.0.3: {}
+ expect-type@1.4.0: {}
+
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
@@ -2044,6 +2938,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.5
+ fflate@0.8.3: {}
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -2108,6 +3004,8 @@ snapshots:
dependencies:
is-glob: 4.0.3
+ globals@17.7.0: {}
+
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
@@ -2135,10 +3033,18 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
ignore@5.3.2: {}
imurmurhash@0.1.4: {}
+ indent-string@4.0.0: {}
+
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -2213,6 +3119,8 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
+ is-potential-custom-element-name@1.0.1: {}
+
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@@ -2265,8 +3173,36 @@ snapshots:
has-symbols: 1.1.0
set-function-name: 2.0.2
+ jiti@2.7.0: {}
+
js-tokens@4.0.0: {}
+ jsdom@29.1.1:
+ dependencies:
+ '@asamuzakjp/css-color': 5.1.11
+ '@asamuzakjp/dom-selector': 7.1.1
+ '@bramus/specificity': 2.4.2
+ '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1)
+ '@exodus/bytes': 1.15.1
+ css-tree: 3.2.1
+ data-urls: 7.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.5.2
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.2
+ undici: 7.28.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
@@ -2350,8 +3286,20 @@ snapshots:
dependencies:
js-tokens: 4.0.0
+ lru-cache@11.5.2: {}
+
+ lz-string@1.5.0: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
math-intrinsics@1.1.0: {}
+ mdn-data@2.27.1: {}
+
+ min-indent@1.0.1: {}
+
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.7
@@ -2362,6 +3310,8 @@ snapshots:
moo@0.5.2: {}
+ mrmime@2.0.1: {}
+
ms@2.1.3: {}
nanoid@3.3.15: {}
@@ -2413,6 +3363,8 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
+ obug@2.1.3: {}
+
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -2436,12 +3388,18 @@ snapshots:
dependencies:
p-limit: 3.1.0
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.0.0
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
path-parse@1.0.7: {}
+ pathe@2.0.3: {}
+
picocolors@1.1.1: {}
picomatch@4.0.5: {}
@@ -2462,6 +3420,12 @@ snapshots:
prettier@3.9.5: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
prop-types-extra@1.1.1(react@16.14.0):
dependencies:
react: 16.14.0
@@ -2514,6 +3478,8 @@ snapshots:
react-is@16.13.1: {}
+ react-is@17.0.2: {}
+
react-overlays@0.7.4(react-dom@16.14.0(react@16.14.0))(react@16.14.0):
dependencies:
classnames: 2.5.1
@@ -2535,6 +3501,11 @@ snapshots:
object-assign: 4.1.1
prop-types: 15.8.1
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -2559,6 +3530,8 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
+ require-from-string@2.0.2: {}
+
resolve@2.0.0-next.5:
dependencies:
is-core-module: 2.16.1
@@ -2607,6 +3580,10 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scheduler@0.19.1:
dependencies:
loose-envify: 1.4.0
@@ -2670,8 +3647,20 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
+ siginfo@2.0.0: {}
+
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
source-map-js@1.2.1: {}
+ stackback@0.0.2: {}
+
+ std-env@4.2.0: {}
+
string.prototype.matchall@4.0.12:
dependencies:
call-bind: 1.0.8
@@ -2716,17 +3705,45 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
supports-preserve-symlinks-flag@1.0.0: {}
+ symbol-tree@3.2.4: {}
+
synckit@0.11.13:
dependencies:
'@pkgr/core': 0.3.6
+ tinybench@2.9.0: {}
+
+ tinyexec@1.2.4: {}
+
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.5)
picomatch: 4.0.5
+ tinyrainbow@3.1.0: {}
+
+ tldts-core@7.4.8: {}
+
+ tldts@7.4.8:
+ dependencies:
+ tldts-core: 7.4.8
+
+ totalist@3.0.1: {}
+
+ tough-cookie@6.0.2:
+ dependencies:
+ tldts: 7.4.8
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tslib@2.8.1:
optional: true
@@ -2767,6 +3784,29 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
+ typescript@7.0.2:
+ optionalDependencies:
+ '@typescript/typescript-aix-ppc64': 7.0.2
+ '@typescript/typescript-darwin-arm64': 7.0.2
+ '@typescript/typescript-darwin-x64': 7.0.2
+ '@typescript/typescript-freebsd-arm64': 7.0.2
+ '@typescript/typescript-freebsd-x64': 7.0.2
+ '@typescript/typescript-linux-arm': 7.0.2
+ '@typescript/typescript-linux-arm64': 7.0.2
+ '@typescript/typescript-linux-loong64': 7.0.2
+ '@typescript/typescript-linux-mips64el': 7.0.2
+ '@typescript/typescript-linux-ppc64': 7.0.2
+ '@typescript/typescript-linux-riscv64': 7.0.2
+ '@typescript/typescript-linux-s390x': 7.0.2
+ '@typescript/typescript-linux-x64': 7.0.2
+ '@typescript/typescript-netbsd-arm64': 7.0.2
+ '@typescript/typescript-netbsd-x64': 7.0.2
+ '@typescript/typescript-openbsd-arm64': 7.0.2
+ '@typescript/typescript-openbsd-x64': 7.0.2
+ '@typescript/typescript-sunos-x64': 7.0.2
+ '@typescript/typescript-win32-arm64': 7.0.2
+ '@typescript/typescript-win32-x64': 7.0.2
+
unbox-primitive@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -2779,11 +3819,15 @@ snapshots:
invariant: 2.2.4
react: 16.14.0
+ undici-types@8.3.0: {}
+
+ undici@7.28.0: {}
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
- vite@8.1.4(esbuild@0.27.7):
+ vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.5
@@ -2791,8 +3835,43 @@ snapshots:
rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
+ '@types/node': 26.1.1
esbuild: 0.27.7
fsevents: 2.3.3
+ jiti: 2.7.0
+
+ vitest@4.1.10(@types/node@26.1.1)(@vitest/ui@4.1.10)(jsdom@29.1.1)(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)):
+ dependencies:
+ '@vitest/expect': 4.1.10
+ '@vitest/mocker': 4.1.10(vite@8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0))
+ '@vitest/pretty-format': 4.1.10
+ '@vitest/runner': 4.1.10
+ '@vitest/snapshot': 4.1.10
+ '@vitest/spy': 4.1.10
+ '@vitest/utils': 4.1.10
+ es-module-lexer: 2.3.0
+ expect-type: 1.4.0
+ magic-string: 0.30.21
+ obug: 2.1.3
+ pathe: 2.0.3
+ picomatch: 4.0.5
+ std-env: 4.2.0
+ tinybench: 2.9.0
+ tinyexec: 1.2.4
+ tinyglobby: 0.2.17
+ tinyrainbow: 3.1.0
+ vite: 8.1.4(@types/node@26.1.1)(esbuild@0.27.7)(jiti@2.7.0)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 26.1.1
+ '@vitest/ui': 4.1.10(vitest@4.1.10)
+ jsdom: 29.1.1
+ transitivePeerDependencies:
+ - msw
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
warning@3.0.0:
dependencies:
@@ -2802,6 +3881,18 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -2847,6 +3938,15 @@ snapshots:
dependencies:
isexe: 2.0.0
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
word-wrap@1.2.5: {}
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
yocto-queue@0.1.0: {}
diff --git a/src/components/EzlinavisComponent.jsx b/src/components/EzlinavisComponent.jsx
index 6a2b6d3..370104b 100644
--- a/src/components/EzlinavisComponent.jsx
+++ b/src/components/EzlinavisComponent.jsx
@@ -137,7 +137,7 @@ class EzlinavisComponent extends Component {
handleListChange(text) {
let list = [];
- let isValid = null;
+ let isValid;
const parser = new Parser(Grammar.fromCompiled(grammar));
try {
parser.feed(text);
diff --git a/src/components/Info.jsx b/src/components/Info.jsx
index 866e873..e188744 100644
--- a/src/components/Info.jsx
+++ b/src/components/Info.jsx
@@ -50,8 +50,8 @@ class Info extends React.Component {
fr.fischer(at)fu-berlin.de.
- ezlinavis is widely used in teaching, and several
- extensive tutorials and teaching modules are available (
+ ezlinavis is widely used in teaching, and several extensive
+ tutorials and teaching modules are available (
doi:10.48694/fortext.3781
diff --git a/src/test-setup.test.ts b/src/test-setup.test.ts
new file mode 100644
index 0000000..94f3462
--- /dev/null
+++ b/src/test-setup.test.ts
@@ -0,0 +1,7 @@
+import {describe, expect, it} from 'vitest';
+
+describe('test scaffolding', () => {
+ it('vitest runs', () => {
+ expect(1 + 1).toBe(2);
+ });
+});
diff --git a/src/test-setup.ts b/src/test-setup.ts
new file mode 100644
index 0000000..bb02c60
--- /dev/null
+++ b/src/test-setup.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom/vitest';
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..2c174e8
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "allowJs": false,
+ "types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"]
+ },
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "exclude": ["node_modules", "build"]
+}
diff --git a/vite.config.mjs b/vite.config.mjs
deleted file mode 100644
index ecd744b..0000000
--- a/vite.config.mjs
+++ /dev/null
@@ -1,14 +0,0 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-
-// https://vitejs.dev/config/
-export default defineConfig({
- base: '/',
- plugins: [react()],
- build: {
- outDir: 'build',
- },
- server: {
- open: true,
- },
-});
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..5253a2a
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,20 @@
+///
+import {defineConfig} from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ base: '/',
+ plugins: [react()],
+ build: {
+ outDir: 'build',
+ },
+ server: {
+ open: true,
+ },
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: ['./src/test-setup.ts'],
+ css: false,
+ },
+});
From 2f7ce117c4ef7e3db402e3e5b567451dc10dc57d Mon Sep 17 00:00:00 2001
From: Carsten Milling
Date: Sat, 11 Jul 2026 11:53:27 +0200
Subject: [PATCH 02/13] Extract pure logic to typed lib modules
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Move getCharacters, getCooccurrences, makeCsv, and the nearley parser
wrapper out of EzlinavisComponent into src/lib/ as strictly-typed TS,
with unit tests. Add ambient declaration for the generated grammar.js
and @types/nearley for parser types. The parseList tests also smoke-run
every bundled example under public/examples/. EzlinavisComponent still
class-based and still wired to react-sigma — component refactor comes
in the next step.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
package.json | 1 +
pnpm-lock.yaml | 8 ++
src/components/EzlinavisComponent.jsx | 105 ++++----------------------
src/grammar.d.ts | 4 +
src/lib/cooccurrences.test.ts | 87 +++++++++++++++++++++
src/lib/cooccurrences.ts | 59 +++++++++++++++
src/lib/parseList.test.ts | 47 ++++++++++++
src/lib/parseList.ts | 29 +++++++
src/test-setup.test.ts | 7 --
tsconfig.json | 2 +-
10 files changed, 249 insertions(+), 100 deletions(-)
create mode 100644 src/grammar.d.ts
create mode 100644 src/lib/cooccurrences.test.ts
create mode 100644 src/lib/cooccurrences.ts
create mode 100644 src/lib/parseList.test.ts
create mode 100644 src/lib/parseList.ts
delete mode 100644 src/test-setup.test.ts
diff --git a/package.json b/package.json
index b30b06a..e758760 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/eslint": "^9.6.1",
+ "@types/nearley": "^2.11.5",
"@types/node": "^26.1.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9d9b4da..4de8bee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,6 +45,9 @@ importers:
'@types/eslint':
specifier: ^9.6.1
version: 9.6.1
+ '@types/nearley':
+ specifier: ^2.11.5
+ version: 2.11.5
'@types/node':
specifier: ^26.1.1
version: 26.1.1
@@ -560,6 +563,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/nearley@2.11.5':
+ resolution: {integrity: sha512-dM7TrN0bVxGGXTYGx4YhGear8ysLO5SOuouAWM9oltjQ3m9oYa13qi8Z1DJp5zxVMPukvQdsrnZmgzpeuTSEQA==}
+
'@types/node@26.1.1':
resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==}
@@ -2334,6 +2340,8 @@ snapshots:
'@types/json-schema@7.0.15': {}
+ '@types/nearley@2.11.5': {}
+
'@types/node@26.1.1':
dependencies:
undici-types: 8.3.0
diff --git a/src/components/EzlinavisComponent.jsx b/src/components/EzlinavisComponent.jsx
index 370104b..862adcf 100644
--- a/src/components/EzlinavisComponent.jsx
+++ b/src/components/EzlinavisComponent.jsx
@@ -1,5 +1,4 @@
import {Component} from 'react';
-import {Parser, Grammar} from 'nearley';
import {Navbar, Nav, NavItem, NavDropdown, MenuItem} from 'react-bootstrap';
import {
Sigma,
@@ -14,7 +13,8 @@ import ForceLink from 'react-sigma/lib/ForceLink';
import Info from './Info';
import ListInput from './ezlinavis/ListInputComponent';
import Csv from './ezlinavis/CsvComponent';
-import grammar from '../grammar';
+import {getCharacters, getCooccurrences, makeCsv} from '../lib/cooccurrences';
+import {parseList} from '../lib/parseList';
import './EzlinavisComponent.css';
@@ -24,85 +24,19 @@ import examples from '../examples.json';
const edgeColor = '#999';
const nodeColor = '#555';
-function getCooccurrences(scenes) {
- const map = {};
- scenes.forEach((scene) => {
- if (!scene.characters) {
- return;
- }
- // make sure each character occurs only once in scene
- const characters = scene.characters
- .map((c) => c.replace(/ +$/, '')) // trim trailing spaces
- .filter((v, i, a) => a.indexOf(v) === i);
- characters.forEach((c, i) => {
- if (i < characters.length - 1) {
- const others = characters.slice(i + 1);
- others.forEach((o) => {
- const pair = [c, o].sort();
- const key = pair.join('|');
- if (map[key]) {
- map[key][2]++;
- } else {
- map[key] = pair.concat(1);
- }
- });
- }
- });
- });
-
- const cooccurrences = [];
- Object.keys(map)
- .sort()
- .forEach((key) => {
- cooccurrences.push(map[key]);
- });
-
- return cooccurrences;
-}
-
-function makeCsv(cooccurrences) {
- let csv = 'Source,Type,Target,Weight\n';
- cooccurrences.forEach((line) => {
- line.splice(1, 0, 'Undirected');
- csv += line.join(',') + '\n';
- });
- return csv;
-}
-
-function getCharacters(scenes) {
- const characters = [];
- scenes.forEach((scene) => {
- if (!scene.characters) {
- return;
- }
- scene.characters.forEach((c) => {
- if (characters.indexOf(c) === -1) {
- characters.push(c);
- }
- });
- });
- return characters;
-}
-
function makeGraph(scenes) {
const characters = getCharacters(scenes);
- const nodes = [];
- characters.forEach((c) => {
- nodes.push({id: c, label: c});
- });
+ const nodes = characters.map((c) => ({id: c, label: c}));
const cooccurrences = getCooccurrences(scenes);
- const edges = [];
- cooccurrences.forEach((cooc) => {
- edges.push({
- id: cooc[0] + '|' + cooc[1],
- source: cooc[0],
- target: cooc[1],
- size: cooc[2],
- // NB: we set the edge color here since the defaultEdgeColor in Sigma
- // settings does not to have any effect
- color: edgeColor,
- });
- });
+ const edges = cooccurrences.map(([source, target, size]) => ({
+ id: `${source}|${target}`,
+ source,
+ target,
+ size,
+ // NB: we set the edge color here since the defaultEdgeColor in Sigma
+ // settings does not to have any effect
+ color: edgeColor,
+ }));
return {nodes, edges};
}
@@ -136,20 +70,7 @@ class EzlinavisComponent extends Component {
}
handleListChange(text) {
- let list = [];
- let isValid;
- const parser = new Parser(Grammar.fromCompiled(grammar));
- try {
- parser.feed(text);
- list = parser.results[0] || {};
- isValid = true;
- } catch (error) {
- // eslint-disable-next-line no-console
- console.log(error);
- isValid = false;
- }
-
- const scenes = list.sections || [];
+ const {isValid, scenes} = parseList(text);
const cooccurrences = getCooccurrences(scenes);
const csv = cooccurrences.length > 0 ? makeCsv(cooccurrences) : null;
const graph = makeGraph(scenes);
diff --git a/src/grammar.d.ts b/src/grammar.d.ts
new file mode 100644
index 0000000..806800c
--- /dev/null
+++ b/src/grammar.d.ts
@@ -0,0 +1,4 @@
+import type {CompiledRules} from 'nearley';
+
+declare const grammar: CompiledRules;
+export default grammar;
diff --git a/src/lib/cooccurrences.test.ts b/src/lib/cooccurrences.test.ts
new file mode 100644
index 0000000..56bf3ca
--- /dev/null
+++ b/src/lib/cooccurrences.test.ts
@@ -0,0 +1,87 @@
+import {describe, expect, it} from 'vitest';
+import {
+ getCharacters,
+ getCooccurrences,
+ makeCsv,
+ type Scene,
+} from './cooccurrences';
+
+describe('getCharacters', () => {
+ it('returns unique characters in order of first appearance', () => {
+ const scenes: Scene[] = [
+ {characters: ['Alice', 'Bob']},
+ {characters: ['Bob', 'Carol', 'Alice']},
+ ];
+ expect(getCharacters(scenes)).toEqual(['Alice', 'Bob', 'Carol']);
+ });
+
+ it('skips scenes with null characters', () => {
+ const scenes: Scene[] = [
+ {characters: null},
+ {characters: ['Alice']},
+ ];
+ expect(getCharacters(scenes)).toEqual(['Alice']);
+ });
+
+ it('returns empty array for empty input', () => {
+ expect(getCharacters([])).toEqual([]);
+ });
+});
+
+describe('getCooccurrences', () => {
+ it('counts pairs across scenes', () => {
+ const scenes: Scene[] = [
+ {characters: ['Alice', 'Bob']},
+ {characters: ['Alice', 'Bob', 'Carol']},
+ ];
+ const result = getCooccurrences(scenes);
+ expect(result).toEqual([
+ ['Alice', 'Bob', 2],
+ ['Alice', 'Carol', 1],
+ ['Bob', 'Carol', 1],
+ ]);
+ });
+
+ it('deduplicates characters within a scene', () => {
+ const scenes: Scene[] = [{characters: ['Alice', 'Alice', 'Bob']}];
+ expect(getCooccurrences(scenes)).toEqual([['Alice', 'Bob', 1]]);
+ });
+
+ it('trims trailing spaces before deduplicating', () => {
+ const scenes: Scene[] = [{characters: ['Alice ', 'Alice', 'Bob']}];
+ expect(getCooccurrences(scenes)).toEqual([['Alice', 'Bob', 1]]);
+ });
+
+ it('skips scenes with null characters', () => {
+ const scenes: Scene[] = [
+ {characters: null},
+ {characters: ['Alice', 'Bob']},
+ ];
+ expect(getCooccurrences(scenes)).toEqual([['Alice', 'Bob', 1]]);
+ });
+
+ it('sorts pairs alphabetically', () => {
+ const scenes: Scene[] = [{characters: ['Bob', 'Alice']}];
+ expect(getCooccurrences(scenes)).toEqual([['Alice', 'Bob', 1]]);
+ });
+
+ it('returns empty array when no cooccurrences', () => {
+ expect(getCooccurrences([{characters: ['Alice']}])).toEqual([]);
+ });
+});
+
+describe('makeCsv', () => {
+ it('produces Gephi-style CSV', () => {
+ const csv = makeCsv([
+ ['Alice', 'Bob', 2],
+ ['Alice', 'Carol', 1],
+ ]);
+ expect(csv).toBe(
+ 'Source,Type,Target,Weight\nAlice,Undirected,Bob,2\nAlice,Undirected,Carol,1\n',
+ );
+ });
+
+ it('emits only the header for empty input', () => {
+ expect(makeCsv([])).toBe('Source,Type,Target,Weight\n');
+ });
+});
diff --git a/src/lib/cooccurrences.ts b/src/lib/cooccurrences.ts
new file mode 100644
index 0000000..6215960
--- /dev/null
+++ b/src/lib/cooccurrences.ts
@@ -0,0 +1,59 @@
+export interface Scene {
+ title?: string;
+ characters: string[] | null;
+}
+
+export type Cooccurrence = [string, string, number];
+
+export function getCharacters(scenes: Scene[]): string[] {
+ const characters: string[] = [];
+ scenes.forEach((scene) => {
+ if (!scene.characters) {
+ return;
+ }
+ scene.characters.forEach((c) => {
+ if (characters.indexOf(c) === -1) {
+ characters.push(c);
+ }
+ });
+ });
+ return characters;
+}
+
+export function getCooccurrences(scenes: Scene[]): Cooccurrence[] {
+ const map: Record = {};
+ scenes.forEach((scene) => {
+ if (!scene.characters) {
+ return;
+ }
+ const characters = scene.characters
+ .map((c) => c.replace(/ +$/, ''))
+ .filter((v, i, a) => a.indexOf(v) === i);
+ characters.forEach((c, i) => {
+ if (i < characters.length - 1) {
+ const others = characters.slice(i + 1);
+ others.forEach((o) => {
+ const pair = [c, o].sort() as [string, string];
+ const key = pair.join('|');
+ if (map[key]) {
+ map[key][2]++;
+ } else {
+ map[key] = [pair[0], pair[1], 1];
+ }
+ });
+ }
+ });
+ });
+
+ return Object.keys(map)
+ .sort()
+ .map((key) => map[key]);
+}
+
+export function makeCsv(cooccurrences: Cooccurrence[]): string {
+ let csv = 'Source,Type,Target,Weight\n';
+ cooccurrences.forEach(([source, target, weight]) => {
+ csv += `${source},Undirected,${target},${weight}\n`;
+ });
+ return csv;
+}
diff --git a/src/lib/parseList.test.ts b/src/lib/parseList.test.ts
new file mode 100644
index 0000000..671f9b5
--- /dev/null
+++ b/src/lib/parseList.test.ts
@@ -0,0 +1,47 @@
+import {readFileSync, readdirSync} from 'node:fs';
+import {join} from 'node:path';
+import {describe, expect, it} from 'vitest';
+import {parseList} from './parseList';
+
+const examplesDir = join(__dirname, '..', '..', 'public', 'examples');
+
+describe('parseList', () => {
+ it('parses a header and one section with characters', () => {
+ const text = 'A play\nby someone\n\n# Act 1\nAlice\nBob\n';
+ const result = parseList(text);
+ expect(result.isValid).toBe(true);
+ expect(result.scenes).toHaveLength(1);
+ expect(result.scenes[0].characters).toEqual(['Alice', 'Bob']);
+ });
+
+ it('accepts multiple sections', () => {
+ const text = '# Act 1\nAlice\n# Act 2\nBob\nCarol\n';
+ const result = parseList(text);
+ expect(result.isValid).toBe(true);
+ expect(result.scenes).toHaveLength(2);
+ expect(result.scenes[1].characters).toEqual(['Bob', 'Carol']);
+ });
+
+ it('treats any level of # as a section separator', () => {
+ const text = '# Act 1\nAlice\n### Act 2\nBob\n';
+ const result = parseList(text);
+ expect(result.isValid).toBe(true);
+ expect(result.scenes).toHaveLength(2);
+ });
+
+ it('returns no scenes when input has no sections', () => {
+ const result = parseList('just a header line\n');
+ expect(result.scenes).toEqual([]);
+ });
+
+ it('parses every bundled example without errors', () => {
+ const files = readdirSync(examplesDir).filter((f) => f.endsWith('.txt'));
+ expect(files.length).toBeGreaterThan(0);
+ for (const file of files) {
+ const text = readFileSync(join(examplesDir, file), 'utf-8');
+ const result = parseList(text);
+ expect(result.isValid, `${file} should parse`).toBe(true);
+ expect(result.scenes.length, `${file} should have sections`).toBeGreaterThan(0);
+ }
+ });
+});
diff --git a/src/lib/parseList.ts b/src/lib/parseList.ts
new file mode 100644
index 0000000..6145948
--- /dev/null
+++ b/src/lib/parseList.ts
@@ -0,0 +1,29 @@
+import {Grammar, Parser} from 'nearley';
+import grammar from '../grammar.js';
+import type {Scene} from './cooccurrences';
+
+export interface ParsedList {
+ header: string | null;
+ sections: Scene[];
+}
+
+export interface ParseResult {
+ isValid: boolean;
+ scenes: Scene[];
+ header: string | null;
+}
+
+export function parseList(text: string): ParseResult {
+ const parser = new Parser(Grammar.fromCompiled(grammar));
+ try {
+ parser.feed(text);
+ const list = (parser.results[0] as ParsedList) ?? {header: null, sections: []};
+ return {
+ isValid: true,
+ scenes: list.sections ?? [],
+ header: list.header ?? null,
+ };
+ } catch {
+ return {isValid: false, scenes: [], header: null};
+ }
+}
diff --git a/src/test-setup.test.ts b/src/test-setup.test.ts
deleted file mode 100644
index 94f3462..0000000
--- a/src/test-setup.test.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import {describe, expect, it} from 'vitest';
-
-describe('test scaffolding', () => {
- it('vitest runs', () => {
- expect(1 + 1).toBe(2);
- });
-});
diff --git a/tsconfig.json b/tsconfig.json
index 2c174e8..2b7e056 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -16,7 +16,7 @@
"skipLibCheck": true,
"noEmit": true,
"allowJs": false,
- "types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"]
+ "types": ["node", "vite/client", "vitest/globals", "@testing-library/jest-dom"]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "build"]
From 118890ac01c1236646a90a5da297146933c7176e Mon Sep 17 00:00:00 2001
From: Carsten Milling
Date: Sat, 11 Jul 2026 12:13:19 +0200
Subject: [PATCH 03/13] Convert components to function + TypeScript
All components moved from class-based JSX to function-based TSX with
hooks (useState, useCallback, useMemo). Entrypoint renamed
src/index.jsx to src/main.tsx and referenced from index.html. The stale
CRA smoke test (src/App.test.jsx) is deleted; prop-types is no longer
imported anywhere.
Staying on React 16 for now: react-bootstrap 0.31 and react-sigma 1.x
are still in place. Ambient module declarations in src/legacy-shims.d.ts
cover those untyped libs until they are removed in the next step.
Tooling: TypeScript pinned to 6.0 (7.x breaks typescript-estree),
typescript-eslint added, @types/react{,-dom} pinned to 16 to match
runtime.
Co-Authored-By: Claude Opus 4.7 (1M context)
---
eslint.config.ts | 22 +-
index.html | 2 +-
package.json | 7 +-
pnpm-lock.yaml | 407 +++++++++---------
src/App.jsx | 15 -
src/App.test.jsx | 8 -
src/App.tsx | 10 +
src/components/EzlinavisComponent.jsx | 212 ---------
src/components/EzlinavisComponent.tsx | 195 +++++++++
src/components/Info.jsx | 108 -----
src/components/Info.tsx | 100 +++++
src/components/ezlinavis/CsvComponent.jsx | 39 --
src/components/ezlinavis/CsvComponent.tsx | 22 +
.../ezlinavis/ListInputComponent.jsx | 39 --
.../ezlinavis/ListInputComponent.tsx | 31 ++
src/legacy-shims.d.ts | 6 +
src/lib/cooccurrences.test.ts | 7 +-
src/lib/parseList.test.ts | 5 +-
src/lib/parseList.ts | 5 +-
src/{index.jsx => main.tsx} | 1 +
20 files changed, 593 insertions(+), 648 deletions(-)
delete mode 100644 src/App.jsx
delete mode 100644 src/App.test.jsx
create mode 100644 src/App.tsx
delete mode 100644 src/components/EzlinavisComponent.jsx
create mode 100644 src/components/EzlinavisComponent.tsx
delete mode 100644 src/components/Info.jsx
create mode 100644 src/components/Info.tsx
delete mode 100644 src/components/ezlinavis/CsvComponent.jsx
create mode 100644 src/components/ezlinavis/CsvComponent.tsx
delete mode 100644 src/components/ezlinavis/ListInputComponent.jsx
create mode 100644 src/components/ezlinavis/ListInputComponent.tsx
create mode 100644 src/legacy-shims.d.ts
rename src/{index.jsx => main.tsx} (69%)
diff --git a/eslint.config.ts b/eslint.config.ts
index 0653ea6..69b6e49 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -2,20 +2,14 @@ import globals from 'globals';
import pluginJs from '@eslint/js';
import pluginReact from 'eslint-plugin-react';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
-import type {Linter} from 'eslint';
+import tseslint from 'typescript-eslint';
-const config: Linter.Config[] = [
- {files: ['**/*.{js,mjs,cjs,jsx}']},
- {languageOptions: {globals: globals.browser}},
- {
- ignores: [
- 'src/grammar.js',
- 'grammar.js',
- 'src/App.test.jsx',
- 'build/**',
- ],
- },
+export default tseslint.config(
+ {files: ['**/*.{js,mjs,cjs,jsx,ts,tsx}']},
+ {languageOptions: {globals: {...globals.browser, ...globals.node}}},
+ {ignores: ['src/grammar.js', 'grammar.js', 'build/**']},
pluginJs.configs.recommended,
+ tseslint.configs.recommended,
pluginReact.configs.flat['jsx-runtime'],
eslintPluginPrettierRecommended,
{
@@ -26,6 +20,4 @@ const config: Linter.Config[] = [
'react/jsx-uses-vars': 1,
},
},
-];
-
-export default config;
+);
diff --git a/index.html b/index.html
index e5cc208..0ae9df6 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,6 @@
Loading...
-
+