From f4f464a56806d360d6b6425ae2a05b9c3ec38924 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Mon, 13 Jul 2026 20:07:12 +0300 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B9?= =?UTF-8?q?=D1=82=D0=B8=20=D0=BD=D0=B0=20=D0=B8=D0=B7=D0=BE=D0=BB=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D1=80=D0=B0=D0=BA=D1=82=20=D0=B3=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавлены независимые adapters для шести exact modes - runtime переведён на JavaScript с отдельными декларациями - generated output перенесён в .svg-sprite - удалены legacy pipeline и встроенный preview --- AGENTS.md | 99 + THIRD_PARTY_NOTICES.md | 26 +- package.json | 6 +- preview/.gitignore | 25 - preview/eslint.config.js | 23 - preview/index.html | 13 - preview/package-lock.json | 3116 ----------------- preview/package.json | 34 - preview/scripts/generate-dev-data.js | 163 - preview/src/App.tsx | 108 - preview/src/app/styles/app.module.css | 64 - preview/src/app/styles/globals.css | 26 - preview/src/app/styles/variables.css | 60 - .../theme/hooks/use-theme.hook.ts | 40 - preview/src/infrastructure/theme/index.ts | 2 - .../theme/styles/theme.module.css | 2 - .../src/infrastructure/theme/theme.infra.tsx | 4 - .../infrastructure/theme/types/theme.type.ts | 11 - preview/src/main.tsx | 28 - preview/src/shared/lib/rgb-to-hex.util.ts | 16 - preview/src/shared/types/index.ts | 6 - preview/src/shared/types/sprites-data.type.ts | 63 - preview/src/ui/banner/banner.ui.tsx | 20 - preview/src/ui/banner/index.ts | 1 - .../src/ui/banner/styles/banner.module.css | 29 - preview/src/ui/banner/types/banner.type.ts | 14 - preview/src/ui/code-block/code-block.ui.tsx | 33 - preview/src/ui/code-block/index.ts | 1 - .../src/ui/code-block/lib/highlight.lib.ts | 68 - .../code-block/styles/code-block.module.css | 85 - .../ui/code-block/types/code-block.type.ts | 18 - .../src/ui/color-picker/color-picker.ui.tsx | 74 - preview/src/ui/color-picker/index.ts | 1 - .../styles/color-picker.module.css | 53 - .../color-picker/types/color-picker.type.ts | 18 - preview/src/ui/icon-card/icon-card.ui.tsx | 29 - preview/src/ui/icon-card/index.ts | 1 - .../ui/icon-card/styles/icon-card.module.css | 37 - .../src/ui/icon-card/types/icon-card.type.ts | 16 - preview/src/ui/icon-grid/icon-grid.ui.tsx | 51 - preview/src/ui/icon-grid/index.ts | 1 - .../ui/icon-grid/styles/icon-grid.module.css | 42 - .../src/ui/icon-grid/types/icon-grid.type.ts | 19 - preview/src/ui/icon-modal/icon-modal.ui.tsx | 235 -- preview/src/ui/icon-modal/index.ts | 1 - .../icon-modal/styles/icon-modal.module.css | 211 -- .../ui/icon-modal/types/icon-modal.type.ts | 19 - preview/src/ui/search-input/index.ts | 1 - .../src/ui/search-input/search-input.ui.tsx | 27 - .../styles/search-input.module.css | 14 - .../search-input/types/search-input.type.ts | 14 - preview/src/vite-env.d.ts | 6 - preview/tsconfig.app.json | 25 - preview/tsconfig.json | 7 - preview/tsconfig.node.json | 24 - preview/vite.config.ts | 32 - src/api/next.ts | 43 + src/api/react.ts | 30 + src/cli.ts | 30 +- src/cli/parse-args.ts | 196 +- src/cli/types.ts | 26 +- src/config.ts | 232 ++ src/core/compiled-artifact.ts | 70 + src/core/generated-markers.ts | 3 + src/core/mode-adapter.ts | 53 + src/core/naming.ts | 17 + src/core/output-writer.ts | 297 ++ src/core/prepare-sprite.ts | 38 + src/core/result.ts | 15 + src/generate.ts | 65 + src/index.ts | 47 +- src/mode-registry.ts | 21 + src/modes/legacy/config.ts | 74 - src/modes/legacy/generate.ts | 52 - src/modes/legacy/index.ts | 2 - src/modes/next-app-turbopack/adapter.ts | 26 + src/modes/next-app-turbopack/output.ts | 170 + src/modes/next-app-webpack/adapter.ts | 18 + src/modes/next-app-webpack/output.ts | 88 + src/modes/next-pages-turbopack/adapter.ts | 18 + src/modes/next-pages-turbopack/output.ts | 88 + src/modes/next-pages-webpack/adapter.ts | 18 + src/modes/next-pages-webpack/output.ts | 88 + src/modes/next/generate.ts | 32 - src/modes/next/index.ts | 6 - src/modes/next/types.ts | 22 - src/modes/react-vite/adapter.ts | 29 + src/modes/react-vite/output.ts | 268 ++ src/modes/react-webpack/adapter.ts | 26 + src/modes/react-webpack/output.ts | 187 + src/modes/react/codegen.ts | 288 -- src/modes/react/config.ts | 108 - src/modes/react/generate.ts | 18 - src/modes/react/index.ts | 7 - src/modes/react/manifest.ts | 120 - src/modes/react/module-generator.ts | 78 - src/modes/react/naming.ts | 25 - src/modes/react/types.ts | 42 - src/modes/react/writer.ts | 172 - src/preview.ts | 185 - src/react/types.ts | 3 +- src/scanner.ts | 45 +- src/targets/index.ts | 33 - src/targets/types.ts | 6 + src/targets/vite.ts | 10 - src/targets/webpack.ts | 12 - src/types.ts | 72 +- svg-sprites.config.ts | 19 - 108 files changed, 2187 insertions(+), 6613 deletions(-) create mode 100644 AGENTS.md delete mode 100644 preview/.gitignore delete mode 100644 preview/eslint.config.js delete mode 100644 preview/index.html delete mode 100644 preview/package-lock.json delete mode 100644 preview/package.json delete mode 100644 preview/scripts/generate-dev-data.js delete mode 100644 preview/src/App.tsx delete mode 100644 preview/src/app/styles/app.module.css delete mode 100644 preview/src/app/styles/globals.css delete mode 100644 preview/src/app/styles/variables.css delete mode 100644 preview/src/infrastructure/theme/hooks/use-theme.hook.ts delete mode 100644 preview/src/infrastructure/theme/index.ts delete mode 100644 preview/src/infrastructure/theme/styles/theme.module.css delete mode 100644 preview/src/infrastructure/theme/theme.infra.tsx delete mode 100644 preview/src/infrastructure/theme/types/theme.type.ts delete mode 100644 preview/src/main.tsx delete mode 100644 preview/src/shared/lib/rgb-to-hex.util.ts delete mode 100644 preview/src/shared/types/index.ts delete mode 100644 preview/src/shared/types/sprites-data.type.ts delete mode 100644 preview/src/ui/banner/banner.ui.tsx delete mode 100644 preview/src/ui/banner/index.ts delete mode 100644 preview/src/ui/banner/styles/banner.module.css delete mode 100644 preview/src/ui/banner/types/banner.type.ts delete mode 100644 preview/src/ui/code-block/code-block.ui.tsx delete mode 100644 preview/src/ui/code-block/index.ts delete mode 100644 preview/src/ui/code-block/lib/highlight.lib.ts delete mode 100644 preview/src/ui/code-block/styles/code-block.module.css delete mode 100644 preview/src/ui/code-block/types/code-block.type.ts delete mode 100644 preview/src/ui/color-picker/color-picker.ui.tsx delete mode 100644 preview/src/ui/color-picker/index.ts delete mode 100644 preview/src/ui/color-picker/styles/color-picker.module.css delete mode 100644 preview/src/ui/color-picker/types/color-picker.type.ts delete mode 100644 preview/src/ui/icon-card/icon-card.ui.tsx delete mode 100644 preview/src/ui/icon-card/index.ts delete mode 100644 preview/src/ui/icon-card/styles/icon-card.module.css delete mode 100644 preview/src/ui/icon-card/types/icon-card.type.ts delete mode 100644 preview/src/ui/icon-grid/icon-grid.ui.tsx delete mode 100644 preview/src/ui/icon-grid/index.ts delete mode 100644 preview/src/ui/icon-grid/styles/icon-grid.module.css delete mode 100644 preview/src/ui/icon-grid/types/icon-grid.type.ts delete mode 100644 preview/src/ui/icon-modal/icon-modal.ui.tsx delete mode 100644 preview/src/ui/icon-modal/index.ts delete mode 100644 preview/src/ui/icon-modal/styles/icon-modal.module.css delete mode 100644 preview/src/ui/icon-modal/types/icon-modal.type.ts delete mode 100644 preview/src/ui/search-input/index.ts delete mode 100644 preview/src/ui/search-input/search-input.ui.tsx delete mode 100644 preview/src/ui/search-input/styles/search-input.module.css delete mode 100644 preview/src/ui/search-input/types/search-input.type.ts delete mode 100644 preview/src/vite-env.d.ts delete mode 100644 preview/tsconfig.app.json delete mode 100644 preview/tsconfig.json delete mode 100644 preview/tsconfig.node.json delete mode 100644 preview/vite.config.ts create mode 100644 src/api/next.ts create mode 100644 src/api/react.ts create mode 100644 src/config.ts create mode 100644 src/core/compiled-artifact.ts create mode 100644 src/core/generated-markers.ts create mode 100644 src/core/mode-adapter.ts create mode 100644 src/core/naming.ts create mode 100644 src/core/output-writer.ts create mode 100644 src/core/prepare-sprite.ts create mode 100644 src/core/result.ts create mode 100644 src/generate.ts create mode 100644 src/mode-registry.ts delete mode 100644 src/modes/legacy/config.ts delete mode 100644 src/modes/legacy/generate.ts delete mode 100644 src/modes/legacy/index.ts create mode 100644 src/modes/next-app-turbopack/adapter.ts create mode 100644 src/modes/next-app-turbopack/output.ts create mode 100644 src/modes/next-app-webpack/adapter.ts create mode 100644 src/modes/next-app-webpack/output.ts create mode 100644 src/modes/next-pages-turbopack/adapter.ts create mode 100644 src/modes/next-pages-turbopack/output.ts create mode 100644 src/modes/next-pages-webpack/adapter.ts create mode 100644 src/modes/next-pages-webpack/output.ts delete mode 100644 src/modes/next/generate.ts delete mode 100644 src/modes/next/index.ts delete mode 100644 src/modes/next/types.ts create mode 100644 src/modes/react-vite/adapter.ts create mode 100644 src/modes/react-vite/output.ts create mode 100644 src/modes/react-webpack/adapter.ts create mode 100644 src/modes/react-webpack/output.ts delete mode 100644 src/modes/react/codegen.ts delete mode 100644 src/modes/react/config.ts delete mode 100644 src/modes/react/generate.ts delete mode 100644 src/modes/react/index.ts delete mode 100644 src/modes/react/manifest.ts delete mode 100644 src/modes/react/module-generator.ts delete mode 100644 src/modes/react/naming.ts delete mode 100644 src/modes/react/types.ts delete mode 100644 src/modes/react/writer.ts delete mode 100644 src/preview.ts delete mode 100644 src/targets/index.ts delete mode 100644 src/targets/vite.ts delete mode 100644 src/targets/webpack.ts delete mode 100644 svg-sprites.config.ts diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a886620 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,99 @@ +# Архитектурные правила + +## Изоляция modes + +Каждый полный mode key является независимым adapter со своим generated-контрактом: + +- `react@vite`; +- `react@webpack`; +- `next@app/turbopack`; +- `next@app/webpack`; +- `next@pages/turbopack`; +- `next@pages/webpack`; +- будущие `vue@*`, `standalone` и другие modes. + +Для каждого exact mode используется отдельный каталог `src/modes//`. Adapter самостоятельно определяет: + +- compile options; +- список и имена generated-файлов; +- runtime JavaScript; +- TypeScript declarations; +- manifest source; +- CSS; +- способ получения asset URL; +- mode-specific result metadata. + +Изменение output одного mode не должно менять output другого mode. Дублирование component, manifest, declaration и CSS codegen между adapters является сознательной ценой изоляции. + +Запрещено: + +- импортировать один `src/modes//` из другого mode; +- создавать общий framework/component/manifest/CSS codegen для нескольких modes; +- создавать общий runtime asset URL generator для нескольких modes; +- ветвиться по generic target внутри adapter; +- писать generated-файлы непосредственно из adapter через `fs`; +- импортировать exact-mode adapters из core. + +Единственное место, которое импортирует все adapters, — `src/mode-registry.ts`. + +## Общий core + +Общими могут быть только mode-neutral данные и инфраструктура: + +- загрузка, merge и валидация config; +- scanner исходных SVG; +- shape IDs и проверка конфликтов; +- низкоуровневый SVG compiler и transformations; +- подготовка нейтрального compiled artifact; +- protocol `ModeAdapter`/`OutputPlan`; +- проверка output paths; +- atomic writer, symlink protection и ownership state; +- logger и базовые result types. + +Core не генерирует JavaScript, declarations, manifest source, CSS или framework-specific exports. Изменение общего compiler может ожидаемо изменить SVG всех modes; изменение generated source должно быть локально одному adapter. + +## Generated-контракт + +Один config разрешается ровно в один mode и один output. Множественные modes не генерируются в один root; orchestration выполняется независимыми config/API/CLI вызовами. + +Runtime генерируется как ESM JavaScript. Типизация добавляется отдельными `.d.ts`; TypeScript/TSX не используется как runtime output. + +Core writer владеет корневым `.gitignore` и `.svg-sprite/state.json`. State не является manifest спрайта: он хранит owner mode, contract version и список управляемых файлов. + +Sprite-level asset, icon data, manifest и facade лежат непосредственно в `.svg-sprite/`. Framework runtime группируется отдельно: React adapters используют `.svg-sprite/react/`, будущие framework adapters получают собственный framework-каталог. + +Каждый adapter имеет собственный `contractVersion`. При изменении его generated-контракта повышается только версия этого adapter. + +Adapter возвращает файлы в памяти. Только core writer проверяет paths и markers, обновляет output и записывает `state.json` последним. + +## Зависимости + +Допустимое направление импортов: + +```text +public API / CLI + -> generate + -> mode-registry + -> один exact-mode adapter + -> core protocols and services +``` + +Обратные и горизонтальные зависимости запрещены: + +```text +core -X-> modes +mode A -X-> mode B +mode A -X-> shared output codegen +``` + +`src/react/` с `SpriteViewer` является отдельным runtime пакета, а не mode adapter. + +## Изменение mode + +При работе с adapter: + +1. Изменяйте только его каталог и mode-neutral protocol, если это действительно необходимо. +2. Не переносите output-логику в core ради устранения дублирования. +3. Не меняйте generated-контракты других adapters автоматически. +4. Повышайте `contractVersion` только изменённого adapter. +5. Проверяйте отсутствие cross-mode imports. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index b630bee..708d9bd 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -1,6 +1,6 @@ # Third-Party Notices -The distributed React entry and preview template include code from the projects listed below. +The distributed React entry includes code from the projects listed below. ## React, React DOM and Scheduler @@ -49,27 +49,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -## clsx - -Copyright (c) Luke Edwards (lukeed.com) - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/package.json b/package.json index 8dbac83..c9b0dae 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "dist/types-*.d.ts", "dist/chunk-*.js", "dist/chunk-*.js.map", - "dist/preview-template.html", "README_RU.md", "docs/en/*.md", "docs/ru/*.md", @@ -39,14 +38,13 @@ "THIRD_PARTY_NOTICES.md" ], "scripts": { - "build": "tsup && npm run build:preview", - "build:preview": "npm ci --prefix preview && npm run build --prefix preview", + "build": "npm run build:package", + "build:package": "tsup", "build:skill": "node skills/svg-sprites/build.mjs", "check:skill": "node skills/svg-sprites/build.mjs --check", "dev": "tsup --watch", "test": "tsup && node --test test/*.test.mjs", "typecheck": "tsc --noEmit", - "sprite": "node dist/cli.js --mode legacy .", "verify": "npm run check:skill && npm run typecheck && npm test", "prepack": "npm run verify && npm run build" }, diff --git a/preview/.gitignore b/preview/.gitignore deleted file mode 100644 index fb1b64a..0000000 --- a/preview/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local -public/dev-data.js - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/preview/eslint.config.js b/preview/eslint.config.js deleted file mode 100644 index 5e6b472..0000000 --- a/preview/eslint.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { defineConfig, globalIgnores } from 'eslint/config' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs.flat.recommended, - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - }, -]) diff --git a/preview/index.html b/preview/index.html deleted file mode 100644 index ef0ad56..0000000 --- a/preview/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - SVG Sprites Preview - - - -
- - - diff --git a/preview/package-lock.json b/preview/package-lock.json deleted file mode 100644 index e8f21dd..0000000 --- a/preview/package-lock.json +++ /dev/null @@ -1,3116 +0,0 @@ -{ - "name": "preview", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "preview", - "version": "0.0.0", - "dependencies": { - "clsx": "^2.1.1", - "react": "^19.2.5", - "react-colorful": "^5.6.1", - "react-dom": "^19.2.5" - }, - "devDependencies": { - "@eslint/js": "^9.39.4", - "@types/node": "^24.12.2", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "eslint": "^9.39.4", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-refresh": "^0.5.2", - "globals": "^17.5.0", - "typescript": "~6.0.2", - "typescript-eslint": "^8.58.2", - "vite": "^8.0.9", - "vite-plugin-singlefile": "^2.3.3" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.139.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", - "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", - "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", - "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", - "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", - "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", - "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", - "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", - "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", - "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", - "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", - "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", - "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", - "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", - "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", - "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", - "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", - "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", - "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/type-utils": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.59.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", - "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", - "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", - "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.7" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.42", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", - "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz", - "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.42", - "caniuse-lite": "^1.0.30001800", - "electron-to-chromium": "^1.5.387", - "node-releases": "^2.0.50", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001803", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz", - "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.389", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", - "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", - "dev": true, - "license": "ISC" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", - "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", - "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": "^9 || ^10" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", - "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", - "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", - "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.5" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/rolldown": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", - "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.139.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.5", - "@rolldown/binding-darwin-arm64": "1.1.5", - "@rolldown/binding-darwin-x64": "1.1.5", - "@rolldown/binding-freebsd-x64": "1.1.5", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", - "@rolldown/binding-linux-arm64-gnu": "1.1.5", - "@rolldown/binding-linux-arm64-musl": "1.1.5", - "@rolldown/binding-linux-ppc64-gnu": "1.1.5", - "@rolldown/binding-linux-s390x-gnu": "1.1.5", - "@rolldown/binding-linux-x64-gnu": "1.1.5", - "@rolldown/binding-linux-x64-musl": "1.1.5", - "@rolldown/binding-openharmony-arm64": "1.1.5", - "@rolldown/binding-wasm32-wasi": "1.1.5", - "@rolldown/binding-win32-arm64-msvc": "1.1.5", - "@rolldown/binding-win32-x64-msvc": "1.1.5" - } - }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", - "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.0", - "@typescript-eslint/parser": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/vite": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", - "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.16", - "rolldown": "~1.1.4", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-plugin-singlefile": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.3.tgz", - "integrity": "sha512-XVnGH0QzbOa8fxRSsHdCarVN1BSBXNi7uLMQYlrGRN5apdHkk62XQWRJhVever0lnfuyBkwn+kvVChdm/OoOUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">18.0.0" - }, - "peerDependencies": { - "rollup": "^4.59.0", - "vite": "^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", - "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - } - } -} diff --git a/preview/package.json b/preview/package.json deleted file mode 100644 index c2c9834..0000000 --- a/preview/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "preview", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev:prepare": "node scripts/generate-dev-data.js", - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "clsx": "^2.1.1", - "react": "^19.2.5", - "react-colorful": "^5.6.1", - "react-dom": "^19.2.5" - }, - "devDependencies": { - "@eslint/js": "^9.39.4", - "@types/node": "^24.12.2", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "eslint": "^9.39.4", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-refresh": "^0.5.2", - "globals": "^17.5.0", - "typescript": "~6.0.2", - "typescript-eslint": "^8.58.2", - "vite": "^8.0.9", - "vite-plugin-singlefile": "^2.3.3" - } -} diff --git a/preview/scripts/generate-dev-data.js b/preview/scripts/generate-dev-data.js deleted file mode 100644 index c6fea13..0000000 --- a/preview/scripts/generate-dev-data.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Генерирует dev-данные для preview из реальных спрайтов основного пакета. - * - * Запуск: node scripts/generate-dev-data.js - * - * Результат: - * public/dev-data.js — window.__SPRITES_DATA__ с метаданными иконок - * public/dev-sprites.svg — инлайновые из всех спрайтов - */ -import fs from 'node:fs' -import path from 'node:path' - -const ROOT = path.resolve(import.meta.dirname, '../..') -const SPRITES_OUTPUT = path.join(ROOT, 'preview/public') -const PREVIEW_PUBLIC = path.join(import.meta.dirname, '../public') - -/** Извлекает id иконок из SVG-спрайта. */ -function extractIconIds(spritePath) { - const content = fs.readFileSync(spritePath, 'utf-8') - const ids = [] - const regex = /<(?:svg|symbol)\b[^>]*\bid="([^"]+)"/g - let match - while ((match = regex.exec(content)) !== null) { - ids.push(match[1]) - } - return ids.sort() -} - -/** Извлекает viewBox из SVG-фрагмента иконки. */ -function extractViewBox(svgFragment) { - const match = svgFragment.match(/viewBox="([^"]+)"/) - if (!match) return null - const parts = match[1].split(/\s+/).map(Number) - if (parts.length !== 4) return null - return { x: parts[0], y: parts[1], width: parts[2], height: parts[3] } -} - -/** Извлекает CSS-переменные из SVG-фрагмента иконки. */ -function extractIconVars(svgFragment) { - const vars = new Map() - const regex = /var\((--icon-color-\d+),\s*([^)]+)\)/g - let match - while ((match = regex.exec(svgFragment)) !== null) { - if (!vars.has(match[1])) { - vars.set(match[1], match[2].trim()) - } - } - return [...vars.entries()].map(([varName, fallback]) => ({ - varName, - fallback, - hex: colorToHex(fallback), - isCurrentColor: fallback.toLowerCase() === 'currentcolor', - })) -} - -/** Извлекает фрагменты иконок из спрайта. */ -function extractIconFragments(spritePath) { - const content = fs.readFileSync(spritePath, 'utf-8') - const fragments = new Map() - const regex = /<(?:svg|symbol)\b[^>]*\bid="([^"]+)"[^>]*>[\s\S]*?<\/(?:svg|symbol)>/g - let match - while ((match = regex.exec(content)) !== null) { - fragments.set(match[1], match[0]) - } - return fragments -} - -/** Конвертирует CSS-цвет в hex. */ -function colorToHex(color) { - const named = { - red: '#ff0000', blue: '#0000ff', green: '#008000', white: '#ffffff', - black: '#000000', yellow: '#ffff00', cyan: '#00ffff', magenta: '#ff00ff', - orange: '#ffa500', purple: '#800080', pink: '#ffc0cb', gray: '#808080', - grey: '#808080', currentcolor: '#000000', - } - const lower = color.toLowerCase().trim() - if (lower.startsWith('#')) { - if (lower.length === 4) return `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}` - return lower - } - return named[lower] || '#000000' -} - -/** Подготавливает спрайт для инлайна — вложенные . */ -function prepareInlineSprite(spritePath) { - let content = fs.readFileSync(spritePath, 'utf-8') - content = content.replace(/<\?xml[^?]*\?>\s*/g, '') - content = content.replace(/' : '' - } - depth++ - if (depth > 1) { - const cleanAttrs = attrs.replace(/\s*xmlns="[^"]*"/g, '') - return `` - } - return `` - }) - return content -} - -// --- Main --- -const spriteFiles = fs.readdirSync(SPRITES_OUTPUT).filter((entry) => { - return entry.endsWith('.sprite.svg') -}) - -const groups = [] -const inlineSprites = [] - -for (const fileName of spriteFiles) { - const spritePath = path.join(SPRITES_OUTPUT, fileName) - const name = fileName.replace('.sprite.svg', '') - - const fragments = extractIconFragments(spritePath) - const ids = extractIconIds(spritePath) - - const icons = ids.map((id) => { - const fragment = fragments.get(id) || '' - return { - id, - group: name, - mode: 'stack', - spriteFile: fileName, - viewBox: extractViewBox(fragment), - vars: extractIconVars(fragment), - } - }) - - groups.push({ name, mode: 'stack', spriteFile: fileName, icons }) - inlineSprites.push(prepareInlineSprite(spritePath)) -} - -// Write dev-data.js — данные + инлайн-спрайты через DOM injection -fs.mkdirSync(PREVIEW_PUBLIC, { recursive: true }) - -const svgContent = inlineSprites.join('\n').replace(/`/g, '\\`').replace(/\$/g, '\\$') - -const dataJs = [ - `window.__SPRITES_DATA__ = ${JSON.stringify({ groups }, null, 2)};`, - '', - '// Inject inline SVG sprites into DOM via DOMParser for correct SVG namespace', - '(function() {', - ` var svg = \`${svgContent}\`;`, - ' var parser = new DOMParser();', - ' var doc = parser.parseFromString("
" + svg + "
", "text/html");', - ' var nodes = doc.body.firstChild.childNodes;', - ' while (nodes.length > 0) {', - ' document.body.insertBefore(nodes[0], document.body.firstChild);', - ' }', - '})();', -].join('\n') - -fs.writeFileSync(path.join(PREVIEW_PUBLIC, 'dev-data.js'), dataJs) - -// Cleanup old separate file if exists -const oldSvg = path.join(PREVIEW_PUBLIC, 'dev-sprites.svg') -if (fs.existsSync(oldSvg)) fs.unlinkSync(oldSvg) - -console.log(`Generated dev data: ${groups.length} groups, ${groups.reduce((s, g) => s + g.icons.length, 0)} icons`) diff --git a/preview/src/App.tsx b/preview/src/App.tsx deleted file mode 100644 index 4b47c09..0000000 --- a/preview/src/App.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useState, useMemo } from 'react' -import { useTheme } from './infrastructure/theme' -import { Banner } from './ui/banner' -import { SearchInput } from './ui/search-input' -import { IconGrid } from './ui/icon-grid' -import { IconModal } from './ui/icon-modal' -import type { SpritesData, IconData } from './shared/types' -import styles from './app/styles/app.module.css' - -declare global { - interface Window { - __SPRITES_DATA__?: SpritesData - } -} - -const MOCK_DATA: SpritesData = { - groups: [], -} - -/** - * Корневой компонент превью SVG-спрайтов. - * - * Используется для: - * - отображения всех иконок из сгенерированных спрайтов - * - просмотра деталей иконки и кода использования - */ -export const App = () => { - const { toggle } = useTheme() - const data = window.__SPRITES_DATA__ ?? MOCK_DATA - const [searchQuery, setSearchQuery] = useState('') - const [selectedIconId, setSelectedIconId] = useState(null) - - const isFileProtocol = location.protocol === 'file:' - - const totalIcons = useMemo( - () => data.groups.reduce((sum, g) => sum + g.icons.length, 0), - [data.groups], - ) - - const selectedIcon = useMemo((): IconData | null => { - if (!selectedIconId) { - return null - } - - for (const group of data.groups) { - const found = group.icons.find((i) => i.id === selectedIconId) - if (found) { - return found - } - } - - return null - }, [selectedIconId, data.groups]) - - const handleCloseModal = (): void => { - setSelectedIconId(null) - } - - return ( - <> - {isFileProtocol && ( - - file:// — Preview opened from local file. External SVG - references won't work in code snippets. Use a local server for full - functionality. - - )} - -
-

SVG Sprites

- {totalIcons} icons -
- - -
-
- - - - - - - - ) -} diff --git a/preview/src/app/styles/app.module.css b/preview/src/app/styles/app.module.css deleted file mode 100644 index 70c2570..0000000 --- a/preview/src/app/styles/app.module.css +++ /dev/null @@ -1,64 +0,0 @@ -.header { - display: flex; - align-items: center; - gap: var(--space-4); - flex-wrap: wrap; - margin-bottom: var(--space-6); -} - -.title { - font-size: 1.5rem; - font-weight: 700; -} - -.count { - font-size: 13px; - color: var(--color-muted); - font-weight: 400; -} - -.toolbar { - display: flex; - gap: var(--space-3); - margin-left: auto; - align-items: center; -} - -.themeButton { - padding: var(--space-2) var(--space-3); - border: 1px solid var(--color-border); - border-radius: var(--radius-2); - background: var(--color-card-bg); - color: var(--color-fg); - cursor: pointer; - font-size: 14px; -} - -.themeButton:hover { - background: var(--color-card-hover); -} - -.footer { - display: flex; - align-items: center; - justify-content: center; - gap: var(--space-3); - margin-top: auto; - padding-top: var(--space-4); - border-top: 1px solid var(--color-border); - color: var(--color-muted); - font-size: 13px; -} - -.footerText { - font-weight: 500; -} - -.footerLink { - color: var(--color-accent); - text-decoration: none; -} - -.footerLink:hover { - text-decoration: underline; -} diff --git a/preview/src/app/styles/globals.css b/preview/src/app/styles/globals.css deleted file mode 100644 index dac9728..0000000 --- a/preview/src/app/styles/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import './variables.css'; - -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: var(--color-bg); - color: var(--color-fg); - padding: var(--space-6); - max-width: 1400px; - margin: 0 auto; - min-height: 100dvh; - display: flex; - flex-direction: column; -} - -#root { - flex: 1; - display: flex; - flex-direction: column; -} diff --git a/preview/src/app/styles/variables.css b/preview/src/app/styles/variables.css deleted file mode 100644 index d9adaa7..0000000 --- a/preview/src/app/styles/variables.css +++ /dev/null @@ -1,60 +0,0 @@ -:root { - --color-bg: #f0f0f3; - --color-fg: #1a1a1a; - --color-card-bg: #ffffff; - --color-card-hover: #eaeaed; - --color-border: #d8d8d8; - --color-accent: #3b82f6; - --color-muted: #888888; - --color-code-bg: #f5f5f5; - - --radius-1: 4px; - --radius-2: 8px; - --radius-3: 12px; - - --space-1: 4px; - --space-2: 8px; - --space-3: 12px; - --space-4: 16px; - --space-5: 20px; - --space-6: 24px; - --space-8: 32px; - --space-10: 40px; - - --icon-size: 128px; - - --checkerboard: conic-gradient( - var(--color-checker-a) 25%, var(--color-checker-b) 0 50%, - var(--color-checker-a) 0 75%, var(--color-checker-b) 0 - ); - --checkerboard-size: 8px 8px; - --color-checker-a: #e9e9e9; - --color-checker-b: #ffffff; - - --font-mono: 'SF Mono', Monaco, Consolas, 'Liberation Mono', monospace; -} - -@media (prefers-color-scheme: dark) { - :root:not([data-theme='light']) { - --color-bg: #1a1a1a; - --color-fg: #e5e5e5; - --color-card-bg: #2a2a2a; - --color-card-hover: #333333; - --color-border: #404040; - --color-code-bg: #2a2a2a; - --color-checker-a: #333333; - --color-checker-b: #2a2a2a; - } -} - -:root[data-theme='dark'] { - --color-bg: #1a1a1a; - --color-fg: #e5e5e5; - --color-card-bg: #2a2a2a; - --color-card-hover: #333333; - --color-border: #404040; - --color-code-bg: #2a2a2a; - --color-checker-a: #333333; - --color-checker-b: #2a2a2a; -} - diff --git a/preview/src/infrastructure/theme/hooks/use-theme.hook.ts b/preview/src/infrastructure/theme/hooks/use-theme.hook.ts deleted file mode 100644 index f4e95c7..0000000 --- a/preview/src/infrastructure/theme/hooks/use-theme.hook.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useState, useCallback, useEffect } from 'react' -import type { Theme } from '../types/theme.type' - -const getSystemTheme = (): Theme => { - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' -} - -/** - * Управляет темой приложения: авто-определение по системе + ручное переключение. - */ -export const useTheme = () => { - const [theme, setTheme] = useState(getSystemTheme) - - const toggle = useCallback(() => { - setTheme((prev) => { - const next: Theme = prev === 'dark' ? 'light' : 'dark' - document.documentElement.dataset.theme = next - return next - }) - }, []) - - useEffect(() => { - // Устанавливаем data-theme при инициализации, чтобы CSS-селекторы - // (включая подсветку кода) работали сразу, а не только после ручного переключения. - document.documentElement.dataset.theme = getSystemTheme() - - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') - - const handleChange = (): void => { - const next = getSystemTheme() - document.documentElement.dataset.theme = next - setTheme(next) - } - - mediaQuery.addEventListener('change', handleChange) - return () => mediaQuery.removeEventListener('change', handleChange) - }, []) - - return { theme, toggle } -} diff --git a/preview/src/infrastructure/theme/index.ts b/preview/src/infrastructure/theme/index.ts deleted file mode 100644 index 0f8c14f..0000000 --- a/preview/src/infrastructure/theme/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { useTheme } from './hooks/use-theme.hook' -export type { Theme } from './types/theme.type' diff --git a/preview/src/infrastructure/theme/styles/theme.module.css b/preview/src/infrastructure/theme/styles/theme.module.css deleted file mode 100644 index c3a2af6..0000000 --- a/preview/src/infrastructure/theme/styles/theme.module.css +++ /dev/null @@ -1,2 +0,0 @@ -.root { -} diff --git a/preview/src/infrastructure/theme/theme.infra.tsx b/preview/src/infrastructure/theme/theme.infra.tsx deleted file mode 100644 index c98a234..0000000 --- a/preview/src/infrastructure/theme/theme.infra.tsx +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Theme infrastructure module — реэкспортирует хук. - * Компонент не требуется, тема управляется через data-theme на documentElement. - */ diff --git a/preview/src/infrastructure/theme/types/theme.type.ts b/preview/src/infrastructure/theme/types/theme.type.ts deleted file mode 100644 index 7afba61..0000000 --- a/preview/src/infrastructure/theme/types/theme.type.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Тема приложения. - */ -export type Theme = 'light' | 'dark' - -/** - * Параметры Theme. - */ -export type ThemeParams = {} - -export type ThemeProps = ThemeParams diff --git a/preview/src/main.tsx b/preview/src/main.tsx deleted file mode 100644 index 17af05b..0000000 --- a/preview/src/main.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './app/styles/globals.css' -import { App } from './App' - -function loadDevScript(): Promise { - return new Promise((resolve, reject) => { - const script = document.createElement('script') - script.src = '/dev-data.js' - script.onload = () => resolve() - script.onerror = reject - document.head.appendChild(script) - }) -} - -async function loadDevData(): Promise { - if (import.meta.env.DEV && !window.__SPRITES_DATA__) { - await loadDevScript() - } -} - -loadDevData().then(() => { - createRoot(document.getElementById('root')!).render( - - - , - ) -}) diff --git a/preview/src/shared/lib/rgb-to-hex.util.ts b/preview/src/shared/lib/rgb-to-hex.util.ts deleted file mode 100644 index 3204eb7..0000000 --- a/preview/src/shared/lib/rgb-to-hex.util.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Конвертирует CSS-цвет (rgb или hex) в hex-формат. - */ -export const rgbToHex = (color: string): string => { - if (color.startsWith('#')) { - return color - } - - const match = color.match(/\d+/g) - - if (match && match.length >= 3) { - return '#' + match.slice(0, 3).map((c) => parseInt(c).toString(16).padStart(2, '0')).join('') - } - - return '#000000' -} diff --git a/preview/src/shared/types/index.ts b/preview/src/shared/types/index.ts deleted file mode 100644 index da40077..0000000 --- a/preview/src/shared/types/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type { - IconVar, - IconData, - SpriteGroup, - SpritesData, -} from './sprites-data.type' diff --git a/preview/src/shared/types/sprites-data.type.ts b/preview/src/shared/types/sprites-data.type.ts deleted file mode 100644 index 3147dbe..0000000 --- a/preview/src/shared/types/sprites-data.type.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * CSS-переменная иконки. - */ -export type IconVar = { - /** Имя CSS-переменной. */ - varName: string - /** Fallback-значение. */ - fallback: string - /** HEX-значение для color picker. */ - hex: string - /** Является ли fallback значением currentColor. */ - isCurrentColor: boolean -} - -/** - * Размеры viewBox иконки. - */ -export type IconViewBox = { - x: number - y: number - width: number - height: number -} - -/** - * Данные одной иконки. - */ -export type IconData = { - /** Идентификатор иконки. */ - id: string - /** Имя группы (папки спрайта). */ - group: string - /** Режим спрайта. */ - mode: 'stack' | 'symbol' - /** Относительный путь к файлу спрайта. */ - spriteFile: string - /** Размеры viewBox иконки. */ - viewBox: IconViewBox | null - /** CSS-переменные иконки. */ - vars: IconVar[] -} - -/** - * Группа спрайтов. - */ -export type SpriteGroup = { - /** Имя группы. */ - name: string - /** Режим спрайта. */ - mode: 'stack' | 'symbol' - /** Относительный путь к файлу спрайта. */ - spriteFile: string - /** Иконки в группе. */ - icons: IconData[] -} - -/** - * Данные для рендера превью. - */ -export type SpritesData = { - /** Группы спрайтов. */ - groups: SpriteGroup[] -} diff --git a/preview/src/ui/banner/banner.ui.tsx b/preview/src/ui/banner/banner.ui.tsx deleted file mode 100644 index b8b4634..0000000 --- a/preview/src/ui/banner/banner.ui.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import cl from 'clsx' -import type { BannerProps } from './types/banner.type' -import styles from './styles/banner.module.css' - -/** - * Баннер-уведомление. - * - * Используется для: - * - предупреждений о file:// протоколе - * - информационных сообщений - */ -export const Banner = (props: BannerProps) => { - const { children, className, variant = 'warn', ...htmlAttr } = props - - return ( -
- {children} -
- ) -} diff --git a/preview/src/ui/banner/index.ts b/preview/src/ui/banner/index.ts deleted file mode 100644 index 4500a84..0000000 --- a/preview/src/ui/banner/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Banner } from './banner.ui' diff --git a/preview/src/ui/banner/styles/banner.module.css b/preview/src/ui/banner/styles/banner.module.css deleted file mode 100644 index 3e99433..0000000 --- a/preview/src/ui/banner/styles/banner.module.css +++ /dev/null @@ -1,29 +0,0 @@ -.root { - display: flex; - align-items: center; - gap: var(--space-2); - padding: 10px var(--space-4); - border-radius: var(--radius-2); - font-size: 13px; - margin-bottom: var(--space-5); -} - -._warn { - background: #fef3c7; - color: #92400e; - border: 1px solid #fde68a; -} - -:root[data-theme='dark'] ._warn { - background: #451a03; - color: #fde68a; - border-color: #92400e; -} - -@media (prefers-color-scheme: dark) { - :root:not([data-theme='light']) ._warn { - background: #451a03; - color: #fde68a; - border-color: #92400e; - } -} diff --git a/preview/src/ui/banner/types/banner.type.ts b/preview/src/ui/banner/types/banner.type.ts deleted file mode 100644 index 0e86e7a..0000000 --- a/preview/src/ui/banner/types/banner.type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { HTMLAttributes } from 'react' - -/** - * Параметры Banner. - */ -export type BannerParams = { - /** Вариант отображения. */ - variant?: 'warn' | 'info' -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = HTMLAttributes - -export type BannerProps = RootAttrs & BannerParams diff --git a/preview/src/ui/code-block/code-block.ui.tsx b/preview/src/ui/code-block/code-block.ui.tsx deleted file mode 100644 index 27bb0e5..0000000 --- a/preview/src/ui/code-block/code-block.ui.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useState, useMemo } from 'react' -import cl from 'clsx' -import { highlight } from './lib/highlight.lib' -import type { CodeBlockProps } from './types/code-block.type' -import styles from './styles/code-block.module.css' - -export const CodeBlock = (props: CodeBlockProps) => { - const { code, language = 'html', copyable = true, className, ...htmlAttr } = props - - const [isCopied, setIsCopied] = useState(false) - - const highlightedHtml = useMemo(() => highlight(code, language), [code, language]) - - const handleCopy = (): void => { - navigator.clipboard.writeText(code).then(() => { - setIsCopied(true) - setTimeout(() => setIsCopied(false), 1500) - }) - } - - return ( -
-
-        
-      
- {copyable && ( - - )} -
- ) -} diff --git a/preview/src/ui/code-block/index.ts b/preview/src/ui/code-block/index.ts deleted file mode 100644 index 91aec98..0000000 --- a/preview/src/ui/code-block/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { CodeBlock } from './code-block.ui' diff --git a/preview/src/ui/code-block/lib/highlight.lib.ts b/preview/src/ui/code-block/lib/highlight.lib.ts deleted file mode 100644 index 73c2031..0000000 --- a/preview/src/ui/code-block/lib/highlight.lib.ts +++ /dev/null @@ -1,68 +0,0 @@ -const ESCAPE_RE = /[&<>"]/g -const ESCAPE_MAP: Record = { '&': '&', '<': '<', '>': '>', '"': '"' } - -const escape = (str: string): string => str.replace(ESCAPE_RE, (c) => ESCAPE_MAP[c]) - -type Token = { kind: string; match: string } -type Rule = [RegExp, string] - -const HTML_RULES: Rule[] = [ - [//g, 'comment'], - [/<\/?[\w-]+/g, 'tag'], - [/<\/?/g, 'punctuation'], - [/\/?>/g, 'punctuation'], - [/[\w-]+(?=\s*=)/g, 'attr'], - [/=/g, 'punctuation'], - [/"[^"]*"|'[^']*'/g, 'string'], -] - -const CSS_RULES: Rule[] = [ - [/\/\*[\s\S]*?\*\//g, 'comment'], - [/\.[\w-]+/g, 'selector'], - [/[\w-]+(?=\s*:)/g, 'property'], - [/[:;]/g, 'punctuation'], - [/\{|\}/g, 'punctuation'], - [/'[^']*'|"[^"]*"/g, 'string'], - [/#[\da-fA-F]{3,8}\b/g, 'color'], - [/\d+(?:\.\d+)?(?:px|em|rem|%|vh|vw|deg|s|ms)?/g, 'number'], - [/url\([^)]*\)/g, 'function'], -] - -const RULES: Record = { html: HTML_RULES, css: CSS_RULES, xml: HTML_RULES } - -const tokenize = (code: string, lang: string): Token[] => { - const rules = RULES[lang] ?? [] - const tokens: Token[] = [] - let pos = 0 - - while (pos < code.length) { - let matched = false - for (const [re, kind] of rules) { - re.lastIndex = pos - const m = re.exec(code) - if (m && m.index === pos) { - tokens.push({ kind, match: m[0] }) - pos += m[0].length - matched = true - break - } - } - if (!matched) { - const last = tokens[tokens.length - 1] - const ch = code[pos] - if (last && last.kind === 'plain') { - last.match += ch - } else { - tokens.push({ kind: 'plain', match: ch }) - } - pos++ - } - } - - return tokens -} - -export const highlight = (code: string, lang: string): string => - tokenize(code, lang) - .map((t) => (t.kind === 'plain' ? escape(t.match) : `${escape(t.match)}`)) - .join('') diff --git a/preview/src/ui/code-block/styles/code-block.module.css b/preview/src/ui/code-block/styles/code-block.module.css deleted file mode 100644 index 51b5f56..0000000 --- a/preview/src/ui/code-block/styles/code-block.module.css +++ /dev/null @@ -1,85 +0,0 @@ -.root { - position: relative; - border-radius: var(--radius-2); - overflow: hidden; - background: var(--color-code-bg); -} - -.pre { - margin: 0; - padding: var(--space-4); - overflow-x: auto; - font-size: 12px; - font-family: var(--font-mono); - line-height: 1.6; -} - -.code { - font-family: var(--font-mono); -} - -:global(:root[data-theme='light']) .code { - --hl-tag: #116329; - --hl-attr: #0550ae; - --hl-string: #0a3069; - --hl-comment: #8b949e; - --hl-punctuation: #6639ba; - --hl-selector: #6639ba; - --hl-property: #0550ae; - --hl-color: #0a3069; - --hl-number: #0550ae; - --hl-function: #0550ae; -} - -:global(:root[data-theme='dark']) .code { - --hl-tag: #7ee787; - --hl-attr: #79c0ff; - --hl-string: #a5d6ff; - --hl-comment: #8b949e; - --hl-punctuation: #d2a8ff; - --hl-selector: #d2a8ff; - --hl-property: #79c0ff; - --hl-color: #a5d6ff; - --hl-number: #79c0ff; - --hl-function: #d2a8ff; -} - -.code :global(.hl-tag), -.code :global(.hl-attr), -.code :global(.hl-string), -.code :global(.hl-comment), -.code :global(.hl-punctuation), -.code :global(.hl-selector), -.code :global(.hl-property), -.code :global(.hl-color), -.code :global(.hl-number), -.code :global(.hl-function) { - color: var(--hl-tag); -} - -.code :global(.hl-attr) { color: var(--hl-attr); } -.code :global(.hl-string) { color: var(--hl-string); } -.code :global(.hl-comment) { color: var(--hl-comment); } -.code :global(.hl-punctuation) { color: var(--hl-punctuation); } -.code :global(.hl-selector) { color: var(--hl-selector); } -.code :global(.hl-property) { color: var(--hl-property); } -.code :global(.hl-color) { color: var(--hl-color); } -.code :global(.hl-number) { color: var(--hl-number); } -.code :global(.hl-function) { color: var(--hl-function); } - -.copyButton { - position: absolute; - top: var(--space-2); - right: var(--space-2); - padding: var(--space-1) var(--space-2); - font-size: 11px; - background: var(--color-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-1); - color: var(--color-muted); - cursor: pointer; -} - -.copyButton:hover { - color: var(--color-fg); -} diff --git a/preview/src/ui/code-block/types/code-block.type.ts b/preview/src/ui/code-block/types/code-block.type.ts deleted file mode 100644 index cbfcf41..0000000 --- a/preview/src/ui/code-block/types/code-block.type.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { HTMLAttributes } from 'react' - -/** - * Параметры CodeBlock. - */ -export type CodeBlockParams = { - /** Код для отображения. */ - code: string - /** Язык подсветки синтаксиса. */ - language?: 'html' | 'css' | 'xml' - /** Показывать кнопку копирования. */ - copyable?: boolean -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = HTMLAttributes - -export type CodeBlockProps = RootAttrs & CodeBlockParams diff --git a/preview/src/ui/color-picker/color-picker.ui.tsx b/preview/src/ui/color-picker/color-picker.ui.tsx deleted file mode 100644 index 7c6f411..0000000 --- a/preview/src/ui/color-picker/color-picker.ui.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useState, useRef, useEffect, useCallback } from 'react' -import { HexColorPicker } from 'react-colorful' -import cl from 'clsx' -import type { ColorPickerProps } from './types/color-picker.type' -import styles from './styles/color-picker.module.css' - -/** - * Выбор цвета с попапом react-colorful и HEX-инпутом. - * - * Используется для: - * - настройки CSS-переменных цвета иконок - * - визуального подбора цвета в модалке - */ -export const ColorPicker = (props: ColorPickerProps) => { - const { value, onValueChange, label, className, ...htmlAttr } = props - - const [isOpen, setIsOpen] = useState(false) - const popoverRef = useRef(null) - const swatchRef = useRef(null) - - const handleToggle = useCallback(() => { - setIsOpen((prev) => !prev) - }, []) - - useEffect(() => { - if (!isOpen) return - - const handleClickOutside = (e: MouseEvent): void => { - const target = e.target as Node - - if ( - popoverRef.current && !popoverRef.current.contains(target) && - swatchRef.current && !swatchRef.current.contains(target) - ) { - setIsOpen(false) - } - } - - document.addEventListener('mousedown', handleClickOutside) - return () => document.removeEventListener('mousedown', handleClickOutside) - }, [isOpen]) - - const handleHexInput = (e: React.ChangeEvent): void => { - const hex = e.target.value - - if (/^#[0-9a-fA-F]{6}$/.test(hex)) { - onValueChange(hex) - } - } - - return ( -
-
- ) -} diff --git a/preview/src/ui/color-picker/index.ts b/preview/src/ui/color-picker/index.ts deleted file mode 100644 index ff2b65f..0000000 --- a/preview/src/ui/color-picker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ColorPicker } from './color-picker.ui' diff --git a/preview/src/ui/color-picker/styles/color-picker.module.css b/preview/src/ui/color-picker/styles/color-picker.module.css deleted file mode 100644 index 6d9682b..0000000 --- a/preview/src/ui/color-picker/styles/color-picker.module.css +++ /dev/null @@ -1,53 +0,0 @@ -.root { - position: relative; - display: inline-flex; - align-items: center; - gap: var(--space-2); -} - -.swatch { - width: 24px; - height: 24px; - border-radius: var(--radius-1); - border: 1px solid var(--color-border); - cursor: pointer; - padding: 0; - background: none; -} - -.label { - font-size: 12px; - color: var(--color-muted); - font-family: var(--font-mono); -} - -.popover { - position: absolute; - top: calc(100% + var(--space-2)); - left: 0; - z-index: 200; - padding: var(--space-3); - background: var(--color-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-2); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); -} - -.hexInput { - display: block; - width: 100%; - margin-top: var(--space-2); - padding: var(--space-1) var(--space-2); - font-size: 12px; - font-family: var(--font-mono); - color: var(--color-fg); - background: var(--color-card-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-1); - outline: none; - text-align: center; -} - -.hexInput:focus { - border-color: var(--color-accent); -} diff --git a/preview/src/ui/color-picker/types/color-picker.type.ts b/preview/src/ui/color-picker/types/color-picker.type.ts deleted file mode 100644 index e65f1e6..0000000 --- a/preview/src/ui/color-picker/types/color-picker.type.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { HTMLAttributes } from 'react' - -/** - * Параметры ColorPicker. - */ -export type ColorPickerParams = { - /** Текущий цвет в HEX. */ - value: string - /** Обработчик изменения цвета. */ - onValueChange: (color: string) => void - /** Подпись под picker-ом. */ - label?: string -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = Omit, 'onChange'> - -export type ColorPickerProps = RootAttrs & ColorPickerParams diff --git a/preview/src/ui/icon-card/icon-card.ui.tsx b/preview/src/ui/icon-card/icon-card.ui.tsx deleted file mode 100644 index 01c86d0..0000000 --- a/preview/src/ui/icon-card/icon-card.ui.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import cl from 'clsx' -import type { IconCardProps } from './types/icon-card.type' -import styles from './styles/icon-card.module.css' - -/** - * Карточка иконки в сетке превью. - * - * Используется для: - * - отображения иконки из спрайта с именем - * - открытия модалки деталей по клику - */ -export const IconCard = (props: IconCardProps) => { - const { iconId, onSelect, className, ...htmlAttr } = props - - const handleClick = (): void => { - onSelect?.(iconId) - } - - return ( -
-
- - - -
- {iconId} -
- ) -} diff --git a/preview/src/ui/icon-card/index.ts b/preview/src/ui/icon-card/index.ts deleted file mode 100644 index d0dce9d..0000000 --- a/preview/src/ui/icon-card/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IconCard } from './icon-card.ui' diff --git a/preview/src/ui/icon-card/styles/icon-card.module.css b/preview/src/ui/icon-card/styles/icon-card.module.css deleted file mode 100644 index f3625c3..0000000 --- a/preview/src/ui/icon-card/styles/icon-card.module.css +++ /dev/null @@ -1,37 +0,0 @@ -.root { - display: flex; - flex-direction: column; - align-items: center; - gap: var(--space-2); - padding: var(--space-4) var(--space-2); - border-radius: var(--radius-2); - background: var(--color-card-bg); - cursor: pointer; - transition: background 0.15s; -} - -.root:hover { - background: var(--color-card-hover); -} - -.iconWrap { - display: flex; - align-items: center; - justify-content: center; - border-radius: var(--radius-1); - background: var(--checkerboard); - background-size: var(--checkerboard-size); -} - -.icon { - width: var(--icon-size); - height: var(--icon-size); - color: var(--color-fg); -} - -.name { - font-size: 13px; - color: var(--color-muted); - text-align: center; - word-break: break-all; -} diff --git a/preview/src/ui/icon-card/types/icon-card.type.ts b/preview/src/ui/icon-card/types/icon-card.type.ts deleted file mode 100644 index f4148e7..0000000 --- a/preview/src/ui/icon-card/types/icon-card.type.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { HTMLAttributes } from 'react' - -/** - * Параметры IconCard. - */ -export type IconCardParams = { - /** Идентификатор иконки. */ - iconId: string - /** Обработчик клика по карточке. */ - onSelect?: (iconId: string) => void -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = Omit, 'onSelect'> - -export type IconCardProps = RootAttrs & IconCardParams diff --git a/preview/src/ui/icon-grid/icon-grid.ui.tsx b/preview/src/ui/icon-grid/icon-grid.ui.tsx deleted file mode 100644 index 0bf99d2..0000000 --- a/preview/src/ui/icon-grid/icon-grid.ui.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import cl from 'clsx' -import { IconCard } from '../icon-card' -import type { IconGridProps } from './types/icon-grid.type' -import styles from './styles/icon-grid.module.css' - -/** - * Сетка иконок, сгруппированная по спрайтам. - * - * Используется для: - * - отображения всех иконок из всех спрайтов - * - фильтрации иконок по поисковому запросу - */ -export const IconGrid = (props: IconGridProps) => { - const { groups, searchQuery = '', onIconSelect, className, ...htmlAttr } = props - - const query = searchQuery.toLowerCase() - - return ( -
- {groups.map((group) => { - const filteredIcons = query - ? group.icons.filter((icon) => icon.id.includes(query)) - : group.icons - - const isGroupHidden = filteredIcons.length === 0 - - return ( -
-

- {group.name} - {group.mode} - {group.icons.length} -

-
- {filteredIcons.map((icon) => ( - - ))} -
-
- ) - })} -
- ) -} diff --git a/preview/src/ui/icon-grid/index.ts b/preview/src/ui/icon-grid/index.ts deleted file mode 100644 index 4222794..0000000 --- a/preview/src/ui/icon-grid/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IconGrid } from './icon-grid.ui' diff --git a/preview/src/ui/icon-grid/styles/icon-grid.module.css b/preview/src/ui/icon-grid/styles/icon-grid.module.css deleted file mode 100644 index 97b8d8d..0000000 --- a/preview/src/ui/icon-grid/styles/icon-grid.module.css +++ /dev/null @@ -1,42 +0,0 @@ -.root { -} - -.group { - margin-bottom: var(--space-10); -} - -.groupHeader { - display: flex; - align-items: center; - gap: var(--space-2); - font-size: 1.1rem; - font-weight: 600; - margin-bottom: var(--space-4); -} - -.badge { - font-size: 11px; - font-weight: 500; - padding: 2px var(--space-2); - border-radius: 10px; - background: var(--color-accent); - color: #ffffff; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.count { - font-size: 13px; - color: var(--color-muted); - font-weight: 400; -} - -.grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - gap: var(--space-3); -} - -._hidden { - display: none; -} diff --git a/preview/src/ui/icon-grid/types/icon-grid.type.ts b/preview/src/ui/icon-grid/types/icon-grid.type.ts deleted file mode 100644 index 78038df..0000000 --- a/preview/src/ui/icon-grid/types/icon-grid.type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { HTMLAttributes } from 'react' -import type { SpriteGroup } from '../../../shared/types' - -/** - * Параметры IconGrid. - */ -export type IconGridParams = { - /** Группы спрайтов для отображения. */ - groups: SpriteGroup[] - /** Строка поиска для фильтрации. */ - searchQuery?: string - /** Обработчик выбора иконки. */ - onIconSelect?: (iconId: string) => void -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = HTMLAttributes - -export type IconGridProps = RootAttrs & IconGridParams diff --git a/preview/src/ui/icon-modal/icon-modal.ui.tsx b/preview/src/ui/icon-modal/icon-modal.ui.tsx deleted file mode 100644 index b085ae1..0000000 --- a/preview/src/ui/icon-modal/icon-modal.ui.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import { useState, useEffect, useCallback, useRef } from 'react' -import cl from 'clsx' -import { CodeBlock } from '../code-block' -import { ColorPicker } from '../color-picker' -import { rgbToHex } from '../../shared/lib/rgb-to-hex.util' -import type { IconModalProps } from './types/icon-modal.type' -import styles from './styles/icon-modal.module.css' - -type TabId = 'react' | 'svg' | 'img' | 'css' - -const TABS: { id: TabId; label: string }[] = [ - { id: 'react', label: 'React' }, - { id: 'svg', label: 'SVG' }, - { id: 'img', label: 'IMG' }, - { id: 'css', label: 'CSS' }, -] - -/** - * Модалка деталей иконки с превью, color pickers и табами кода. - * - * Превью иконки рендерится тем же способом, что выбран в табе: - * - React / SVG — через - * - IMG — через - * - CSS — через mask-image на
- */ -export const IconModal = (props: IconModalProps) => { - const { icon, onClose, defaultSprite, className, ...htmlAttr } = props - - const [activeTab, setActiveTab] = useState('react') - const [colors, setColors] = useState>({}) - const [cssColor, setCssColor] = useState('#000000') - const iconWrapRef = useRef(null) - - const handleOverlayClick = useCallback((e: React.MouseEvent) => { - if (e.target === e.currentTarget) { - onClose() - } - }, [onClose]) - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent): void => { - if (e.key === 'Escape') { - onClose() - } - } - - if (icon) { - document.addEventListener('keydown', handleKeyDown) - } - - return () => document.removeEventListener('keydown', handleKeyDown) - }, [icon, onClose]) - - useEffect(() => { - if (!icon) return - - const fg = getComputedStyle(document.documentElement).getPropertyValue('--color-fg').trim() - const fgHex = rgbToHex(fg) - - const initialColors: Record = {} - icon.vars.forEach((v) => { - initialColors[v.varName] = v.isCurrentColor ? fgHex : v.hex - }) - - setColors(initialColors) - setCssColor(fgHex) - setActiveTab('react') - - if (iconWrapRef.current) { - iconWrapRef.current.removeAttribute('style') - } - }, [icon]) - - if (!icon) { - return null - } - - const spriteRef = `${icon.spriteFile}#${icon.id}` - - const isDefaultSprite = icon.group === defaultSprite - const codeReact = isDefaultSprite - ? `` - : `` - const codeSvg = `\n \n` - const codeImg = `${icon.id}` - const codeCss = `.${icon.id} {\n width: 24px;\n height: 24px;\n mask: url('${spriteRef}') no-repeat center / contain;\n -webkit-mask: url('${spriteRef}') no-repeat center / contain;\n background-color: ${cssColor};\n}` - - const codeByTab: Record = { - react: { code: codeReact, language: 'html' }, - svg: { code: codeSvg, language: 'html' }, - img: { code: codeImg, language: 'html' }, - css: { code: codeCss, language: 'css' }, - } - - const handleColorChange = (varName: string, value: string): void => { - setColors((prev) => ({ ...prev, [varName]: value })) - iconWrapRef.current?.style.setProperty(varName, value) - } - - const renderPreview = () => { - switch (activeTab) { - case 'react': - case 'svg': - return ( - - - - ) - - case 'img': - return ( - {icon.id} - ) - - case 'css': - return ( -
- ) - } - } - - const supportsColorChange = activeTab === 'react' || activeTab === 'svg' - const isMono = icon.vars.length > 0 && icon.vars.every((v) => v.isCurrentColor) - - return ( -
-
- - -
-
- {renderPreview()} -
-
- -
-
{icon.id}
- {icon.viewBox && ( - - {icon.viewBox.width} × {icon.viewBox.height} - - )} -
- -
- {icon.vars.length > 0 && supportsColorChange ? ( - <> -
- {isMono ? ( - - Иконка наследует цвет текста или задаётся точечно через CSS-переменную. - - ) : ( - - Многоцветная иконка — каждый цвет задаётся отдельной CSS-переменной. - - )} -
-
CSS Variables
- {icon.vars.map((v) => ( - handleColorChange(v.varName, color)} - label={`${v.varName}: ${v.fallback}`} - className={styles.varRow} - /> - ))} - - ) : activeTab === 'img' ? ( -
- Управление цветом в режиме IMG невозможно. <img> изолирует SVG — CSS-переменные - и currentColor не проникают внутрь. Подходит для многоцветных изображений с фиксированными цветами. -
- ) : activeTab === 'css' ? ( - <> -
- В режиме CSS mask иконка монохромная — цвет задаётся через background-color. - CSS-переменные спрайта не поддерживаются. -
- - - ) : ( - No color variables - )} -
- -
- {TABS.map((tab) => ( - - ))} -
- - {TABS.map((tab) => ( -
- -
- ))} -
-
- ) -} diff --git a/preview/src/ui/icon-modal/index.ts b/preview/src/ui/icon-modal/index.ts deleted file mode 100644 index 377c6e9..0000000 --- a/preview/src/ui/icon-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IconModal } from './icon-modal.ui' diff --git a/preview/src/ui/icon-modal/styles/icon-modal.module.css b/preview/src/ui/icon-modal/styles/icon-modal.module.css deleted file mode 100644 index dfeaa85..0000000 --- a/preview/src/ui/icon-modal/styles/icon-modal.module.css +++ /dev/null @@ -1,211 +0,0 @@ -.overlay { - position: fixed; - inset: 0; - z-index: 100; - display: flex; - align-items: flex-start; - justify-content: center; - padding: var(--space-10) 0; - overflow-y: auto; - background: rgba(0, 0, 0, 0.5); -} - -.root { - position: relative; - width: 90%; - max-width: 560px; - padding: var(--space-6); - background: var(--color-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-3); -} - -.closeButton { - position: absolute; - top: var(--space-3); - right: var(--space-3); - padding: var(--space-1) var(--space-2); - background: none; - border: none; - color: var(--color-fg); - font-size: 20px; - cursor: pointer; - line-height: 1; -} - -.iconWrap { - display: flex; - align-items: center; - justify-content: center; - padding: var(--space-6); - margin-bottom: var(--space-4); - background: var(--color-card-bg); - border-radius: var(--radius-2); -} - -.iconViewBox { - display: flex; - align-items: center; - justify-content: center; - border-radius: var(--radius-1); - background: var(--checkerboard); - background-size: var(--checkerboard-size); -} - -.icon, -.iconImg, -.iconCss { - width: 256px; - height: 256px; -} - -.icon { - color: var(--color-fg); -} - -.iconImg { - object-fit: contain; -} - -.iconCss { - background-color: var(--color-fg); -} - -.nameRow { - display: flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - margin-bottom: var(--space-4); -} - -.name { - font-size: 1rem; - font-weight: 600; -} - -.viewBoxBadge { - font-size: 11px; - font-weight: 500; - padding: 2px var(--space-2); - border-radius: 10px; - background: var(--color-card-bg); - border: 1px solid var(--color-border); - color: var(--color-muted); - white-space: nowrap; -} - -.varsSection { - margin-bottom: var(--space-5); -} - -.varsTitle { - font-size: 12px; - font-weight: 600; - color: var(--color-muted); - text-transform: uppercase; - letter-spacing: 0.5px; - margin-bottom: var(--space-2); -} - -.varRow { - display: flex; - align-items: center; - gap: var(--space-2); - margin-bottom: var(--space-2); -} - - - -.noVars { - font-size: 12px; - color: var(--color-muted); - font-style: italic; -} - -.hint { - font-size: 12px; - line-height: 1.5; - color: var(--color-muted); - padding: var(--space-2) var(--space-3); - background: var(--color-card-bg); - border-radius: var(--radius-1); - border-left: 3px solid var(--color-accent); -} - -.colorHint { - margin-bottom: var(--space-3); - padding: var(--space-2) var(--space-3); - font-size: 12px; - line-height: 1.5; - color: var(--color-muted); - background: var(--color-card-bg); - border-radius: var(--radius-1); - border-left: 3px solid var(--color-accent); -} - -.colorHintTitle { - font-size: 12px; - font-weight: 600; - color: var(--color-muted); - margin-bottom: var(--space-1); -} - -.colorHintRow { - display: flex; - align-items: baseline; - gap: var(--space-2); - margin-top: var(--space-1); -} - -.colorHintCode { - font-family: var(--font-mono); - font-size: 11px; - padding: 2px var(--space-1); - background: var(--color-bg); - border-radius: 3px; - white-space: nowrap; -} - -.colorHintDesc { - font-size: 11px; - color: var(--color-muted); -} - -.cssColorRow { - display: flex; - align-items: center; - gap: var(--space-2); - margin-top: var(--space-3); -} - -.tabs { - display: flex; - gap: 0; - border-bottom: 1px solid var(--color-border); - margin-bottom: var(--space-3); -} - -.tab { - padding: var(--space-2) var(--space-4); - font-size: 12px; - font-weight: 600; - cursor: pointer; - border: none; - border-bottom: 2px solid transparent; - color: var(--color-muted); - background: none; -} - -.tab._active { - color: var(--color-accent); - border-bottom-color: var(--color-accent); -} - -.tabContent { - display: none; -} - -.tabContent._active { - display: block; -} diff --git a/preview/src/ui/icon-modal/types/icon-modal.type.ts b/preview/src/ui/icon-modal/types/icon-modal.type.ts deleted file mode 100644 index e325d78..0000000 --- a/preview/src/ui/icon-modal/types/icon-modal.type.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { HTMLAttributes } from 'react' -import type { IconData } from '../../../shared/types' - -/** - * Параметры IconModal. - */ -export type IconModalParams = { - /** Данные иконки для отображения. null — модалка закрыта. */ - icon: IconData | null - /** Имя спрайта по умолчанию (первый из конфига). */ - defaultSprite?: string - /** Обработчик закрытия модалки. */ - onClose: () => void -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = HTMLAttributes - -export type IconModalProps = RootAttrs & IconModalParams diff --git a/preview/src/ui/search-input/index.ts b/preview/src/ui/search-input/index.ts deleted file mode 100644 index c60a639..0000000 --- a/preview/src/ui/search-input/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SearchInput } from './search-input.ui' diff --git a/preview/src/ui/search-input/search-input.ui.tsx b/preview/src/ui/search-input/search-input.ui.tsx deleted file mode 100644 index be42a0e..0000000 --- a/preview/src/ui/search-input/search-input.ui.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import cl from 'clsx' -import type { SearchInputProps } from './types/search-input.type' -import styles from './styles/search-input.module.css' - -/** - * Поле поиска. - * - * Используется для: - * - фильтрации иконок по имени - */ -export const SearchInput = (props: SearchInputProps) => { - const { className, onValueChange, ...htmlAttr } = props - - const handleChange = (e: React.ChangeEvent): void => { - onValueChange?.(e.target.value) - } - - return ( - - ) -} diff --git a/preview/src/ui/search-input/styles/search-input.module.css b/preview/src/ui/search-input/styles/search-input.module.css deleted file mode 100644 index c978e94..0000000 --- a/preview/src/ui/search-input/styles/search-input.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.root { - padding: var(--space-2) var(--space-3); - border: 1px solid var(--color-border); - border-radius: var(--radius-2); - background: var(--color-card-bg); - color: var(--color-fg); - font-size: 14px; - width: 200px; - outline: none; -} - -.root:focus { - border-color: var(--color-accent); -} diff --git a/preview/src/ui/search-input/types/search-input.type.ts b/preview/src/ui/search-input/types/search-input.type.ts deleted file mode 100644 index c674f4f..0000000 --- a/preview/src/ui/search-input/types/search-input.type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { InputHTMLAttributes } from 'react' - -/** - * Параметры SearchInput. - */ -export type SearchInputParams = { - /** Обработчик изменения значения поиска. */ - onValueChange?: (value: string) => void -} - -/** HTML-атрибуты корневого элемента. */ -type RootAttrs = Omit, 'onChange'> - -export type SearchInputProps = RootAttrs & SearchInputParams diff --git a/preview/src/vite-env.d.ts b/preview/src/vite-env.d.ts deleted file mode 100644 index feb7ba8..0000000 --- a/preview/src/vite-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// - -declare module '*.module.css' { - const classes: { readonly [key: string]: string } - export default classes -} diff --git a/preview/tsconfig.app.json b/preview/tsconfig.app.json deleted file mode 100644 index 7f42e5f..0000000 --- a/preview/tsconfig.app.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "es2023", - "lib": ["ES2023", "DOM"], - "module": "esnext", - "types": ["vite/client"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} diff --git a/preview/tsconfig.json b/preview/tsconfig.json deleted file mode 100644 index 1ffef60..0000000 --- a/preview/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/preview/tsconfig.node.json b/preview/tsconfig.node.json deleted file mode 100644 index d3c52ea..0000000 --- a/preview/tsconfig.node.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "es2023", - "lib": ["ES2023"], - "module": "esnext", - "types": ["node"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} diff --git a/preview/vite.config.ts b/preview/vite.config.ts deleted file mode 100644 index 891521d..0000000 --- a/preview/vite.config.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { resolve } from 'node:path' -import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'node:fs' -import { defineConfig } from 'vite' -import type { Plugin } from 'vite' -import react from '@vitejs/plugin-react' -import { viteSingleFile } from 'vite-plugin-singlefile' - -/** Переименовывает index.html → preview-template.html после сборки. */ -function renameOutput(): Plugin { - return { - name: 'rename-preview-template', - closeBundle() { - const outDir = resolve(__dirname, '../dist') - const src = resolve(outDir, 'index.html') - const dest = resolve(outDir, 'preview-template.html') - - if (existsSync(src)) { - const content = readFileSync(src, 'utf-8') - writeFileSync(dest, content) - unlinkSync(src) - } - }, - } -} - -export default defineConfig({ - plugins: [react(), viteSingleFile(), renameOutput()], - build: { - outDir: resolve(__dirname, '../dist'), - emptyOutDir: false, - }, -}) diff --git a/src/api/next.ts b/src/api/next.ts new file mode 100644 index 0000000..58b1925 --- /dev/null +++ b/src/api/next.ts @@ -0,0 +1,43 @@ +import type { SpriteGenerationBaseResult } from '../core/result.js' +import { generateSprite } from '../generate.js' +import type { + NextAssetTarget, + NextBundler, + NextRouter, +} from '../targets/types.js' +import type { SpriteConfig } from '../types.js' + +/** @deprecated Используйте единый SpriteConfig. */ +export type NextSpriteConfig = Omit & { + mode?: NextAssetTarget +} + +export type NextSpriteGenerationOptions = { + router: NextRouter + bundler: NextBundler +} + +export type NextSpriteGenerationResult = SpriteGenerationBaseResult< + NextAssetTarget, + NextAssetTarget +> & { + router: NextRouter + bundler: NextBundler +} + +/** Генерирует Next.js sprite-модуль для явно выбранных router и bundler. */ +export async function generateNextSprite( + source: string, + options: NextSpriteGenerationOptions, +): Promise { + if (!options || (options.router !== 'app' && options.router !== 'pages')) { + throw new Error(`Unsupported Next.js router: ${String(options?.router)}`) + } + if (options.bundler !== 'turbopack' && options.bundler !== 'webpack') { + throw new Error(`Unsupported Next.js bundler: ${String(options.bundler)}`) + } + + return generateSprite(source, { + mode: `next@${options.router}/${options.bundler}`, + }) as Promise +} diff --git a/src/api/react.ts b/src/api/react.ts new file mode 100644 index 0000000..8df2bf7 --- /dev/null +++ b/src/api/react.ts @@ -0,0 +1,30 @@ +import { generateSprite } from '../generate.js' +import type { ReactAssetTarget, ReactSpriteMode } from '../targets/types.js' +import type { ResolvedSpriteConfig, SpriteConfig } from '../types.js' +import type { SpriteGenerationBaseResult } from '../core/result.js' + +/** @deprecated Используйте единый SpriteConfig. */ +export type ReactSpriteConfig = Omit & { + mode?: ReactSpriteMode +} + +/** @deprecated Используйте единый ResolvedSpriteConfig. */ +export type ResolvedReactSpriteConfig = Omit & { + mode: ReactSpriteMode +} + +export type ReactSpriteGenerationResult = SpriteGenerationBaseResult< + ReactSpriteMode, + ReactAssetTarget +> + +/** Генерирует React sprite-модуль для явно выбранного asset target. */ +export function generateReactSprite( + source: string, + target: ReactAssetTarget, +): Promise { + if (target !== 'vite' && target !== 'webpack') { + throw new Error(`Unsupported React asset target: ${String(target)}`) + } + return generateSprite(source, { mode: `react@${target}` }) as Promise +} diff --git a/src/cli.ts b/src/cli.ts index 2925ed6..6ec6240 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,17 +1,7 @@ #!/usr/bin/env node -import path from 'node:path' import { CLI_USAGE, parseCliArgs } from './cli/parse-args.js' +import { generateSprite } from './generate.js' import { log } from './logger.js' -import { generateNextSprite } from './modes/next/generate.js' -import { generateReactSprite } from './modes/react/generate.js' -import { loadLegacyConfig } from './modes/legacy/config.js' -import { generateLegacy } from './modes/legacy/generate.js' - -async function runLegacy(spritePath: string): Promise { - const rootDir = path.resolve(spritePath) - const config = await loadLegacyConfig(rootDir) - await generateLegacy(config) -} async function main() { try { @@ -22,23 +12,7 @@ async function main() { return } - switch (args.mode) { - case 'legacy': - await runLegacy(args.path) - return - case 'react': - await generateReactSprite(args.path, args.target) - return - case 'next': { - const [, routerAndBundler] = args.target.split('@') - const [router, bundler] = routerAndBundler.split('/') - await generateNextSprite(args.path, { - router: router as 'app' | 'pages', - bundler: bundler as 'turbopack' | 'webpack', - }) - return - } - } + await generateSprite(args.path, args.overrides) } catch (error) { log.error(error instanceof Error ? error.message : String(error)) process.exit(1) diff --git a/src/cli/parse-args.ts b/src/cli/parse-args.ts index d88a4e0..c7cbab5 100644 --- a/src/cli/parse-args.ts +++ b/src/cli/parse-args.ts @@ -1,119 +1,143 @@ -import type { NextAssetTarget, ReactAssetTarget } from '../targets/types.js' +import { isSpriteMode } from '../config.js' +import type { SpriteConfig, TransformOptions } from '../types.js' import type { CliArgs } from './types.js' -const REACT_TARGETS = new Set(['vite', 'webpack']) -const NEXT_TARGETS = new Set([ - 'next@app/turbopack', - 'next@app/webpack', - 'next@pages/turbopack', - 'next@pages/webpack', -]) - export const CLI_USAGE = [ 'Usage:', - ' svg-sprites --mode ', + ' svg-sprites [options] ', + '', + 'Config files:', + ' Any explicitly provided .js, .json or .ts file', + ' A directory enables config-less generation from CLI options', '', 'Modes:', - ' legacy Generate sprites through the legacy pipeline', - ' react@vite Generate a React module for Vite', - ' react@webpack Generate a React module for Webpack 5', - ' next@app/turbopack Generate an App Router module for Turbopack', - ' next@app/webpack Generate an App Router module for Webpack 5', - ' next@pages/turbopack Generate a Pages Router module for Turbopack', - ' next@pages/webpack Generate a Pages Router module for Webpack 5', + ' react@vite', + ' react@webpack', + ' next@app/turbopack', + ' next@app/webpack', + ' next@pages/turbopack', + ' next@pages/webpack', + '', + 'Options:', + ' --mode ', + ' --name ', + ' --description ', + ' --input-folder ', + ' --input-file Repeat for multiple files', + ' --[no-]remove-size', + ' --[no-]replace-colors', + ' --[no-]add-transition', + ' --[no-]generated-notice', ].join('\n') -export function parseCliArgs(argv: string[]): CliArgs | { help: true } { - if (argv.includes('--help') || argv.includes('-h')) { - return { help: true } +function optionValue(argv: string[], index: number, option: string): [string, number] { + const argument = argv[index] + const inlinePrefix = `${option}=` + if (argument.startsWith(inlinePrefix)) { + const value = argument.slice(inlinePrefix.length) + if (!value) throw new Error(`Missing value for ${option}.`) + return [value, index] } - let modeValue: string | undefined + const value = argv[index + 1] + if (!value || value.startsWith('-')) throw new Error(`Missing value for ${option}.`) + return [value, index + 1] +} + +function setTransform( + overrides: SpriteConfig, + option: keyof TransformOptions, + value: boolean, +): void { + overrides.transform = { + ...overrides.transform, + [option]: value, + } +} + +export function parseCliArgs(argv: string[]): CliArgs | { help: true } { + if (argv.includes('--help') || argv.includes('-h')) return { help: true } + const positional: string[] = [] + const overrides: SpriteConfig = {} for (let index = 0; index < argv.length; index++) { const argument = argv[index] - if (argument === '--mode') { - modeValue = argv[index + 1] - index++ + if (argument === '--mode' || argument.startsWith('--mode=')) { + const [value, nextIndex] = optionValue(argv, index, '--mode') + if (!isSpriteMode(value)) throw new Error(`Unsupported sprite mode: ${value}.\n\n${CLI_USAGE}`) + overrides.mode = value + index = nextIndex + continue + } + if (argument === '--name' || argument.startsWith('--name=')) { + const [value, nextIndex] = optionValue(argv, index, '--name') + overrides.name = value + index = nextIndex + continue + } + if (argument === '--description' || argument.startsWith('--description=')) { + const [value, nextIndex] = optionValue(argv, index, '--description') + overrides.description = value + index = nextIndex + continue + } + if (argument === '--input-folder' || argument.startsWith('--input-folder=')) { + const [value, nextIndex] = optionValue(argv, index, '--input-folder') + overrides.inputFolder = value + index = nextIndex + continue + } + if (argument === '--input-file' || argument.startsWith('--input-file=')) { + const [value, nextIndex] = optionValue(argv, index, '--input-file') + overrides.inputFiles = [...(overrides.inputFiles ?? []), value] + index = nextIndex continue } - if (argument.startsWith('--mode=')) { - modeValue = argument.slice('--mode='.length) - continue + switch (argument) { + case '--remove-size': + setTransform(overrides, 'removeSize', true) + continue + case '--no-remove-size': + setTransform(overrides, 'removeSize', false) + continue + case '--replace-colors': + setTransform(overrides, 'replaceColors', true) + continue + case '--no-replace-colors': + setTransform(overrides, 'replaceColors', false) + continue + case '--add-transition': + setTransform(overrides, 'addTransition', true) + continue + case '--no-add-transition': + setTransform(overrides, 'addTransition', false) + continue + case '--generated-notice': + overrides.generatedNotice = true + continue + case '--no-generated-notice': + overrides.generatedNotice = false + continue } if (argument.startsWith('-')) { throw new Error(`Unknown argument: ${argument}\n\n${CLI_USAGE}`) } - positional.push(argument) } - if (!modeValue) { - throw new Error(`Missing required argument: --mode\n\n${CLI_USAGE}`) - } - if (positional.length === 0) { - throw new Error(`Missing sprite path.\n\n${CLI_USAGE}`) + throw new Error(`Missing sprite config file or module directory.\n\n${CLI_USAGE}`) } - if (positional.length > 1) { - throw new Error(`Expected one sprite path, received: ${positional.join(', ')}`) + throw new Error(`Expected one config file or module directory, received: ${positional.join(', ')}`) } - if (modeValue === 'legacy') { - return { - mode: 'legacy', - path: positional[0], - } + return { + path: positional[0], + overrides, } - - if (modeValue === 'react') { - throw new Error( - 'React mode requires a target. Supported: react@vite, react@webpack.', - ) - } - - if (modeValue.startsWith('react@')) { - const target = modeValue.slice('react@'.length) - - if (!REACT_TARGETS.has(target as ReactAssetTarget)) { - throw new Error( - `Unsupported React target: ${target}. Supported: ${[...REACT_TARGETS].join(', ')}.`, - ) - } - - return { - mode: 'react', - path: positional[0], - target: target as ReactAssetTarget, - } - } - - if (modeValue === 'next') { - throw new Error( - `Next.js mode requires a router and bundler. Supported: ${[...NEXT_TARGETS].join(', ')}.`, - ) - } - - if (modeValue.startsWith('next@')) { - if (!NEXT_TARGETS.has(modeValue as NextAssetTarget)) { - throw new Error( - `Unsupported Next.js target: ${modeValue}. Supported: ${[...NEXT_TARGETS].join(', ')}.`, - ) - } - - return { - mode: 'next', - path: positional[0], - target: modeValue as NextAssetTarget, - } - } - - throw new Error( - `Unknown mode: ${modeValue}\nSupported modes: legacy, react@vite, react@webpack, ${[...NEXT_TARGETS].join(', ')}`, - ) } diff --git a/src/cli/types.ts b/src/cli/types.ts index 0b29623..59c1e0a 100644 --- a/src/cli/types.ts +++ b/src/cli/types.ts @@ -1,26 +1,6 @@ -import type { NextAssetTarget, ReactAssetTarget } from '../targets/types.js' +import type { SpriteConfig } from '../types.js' -/** Корневой режим генерации, определяющий структуру создаваемых файлов. */ -export type GenerationMode = 'legacy' | 'next' | 'react' - -/** Аргументы legacy pipeline. */ -export type LegacyCliArgs = { - mode: 'legacy' +export type CliArgs = { path: string + overrides: SpriteConfig } - -/** Аргументы React pipeline с обязательной средой обработки SVG asset. */ -export type ReactCliArgs = { - mode: 'react' - path: string - target: ReactAssetTarget -} - -/** Аргументы Next.js pipeline с явно выбранными роутером и сборщиком. */ -export type NextCliArgs = { - mode: 'next' - path: string - target: NextAssetTarget -} - -export type CliArgs = LegacyCliArgs | NextCliArgs | ReactCliArgs diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..0da06a2 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,232 @@ +import fs from 'node:fs' +import path from 'node:path' +import { createJiti } from 'jiti' +import { toKebabCase, validateSpriteName } from './core/naming.js' +import type { SpriteMode } from './targets/types.js' +import type { ResolvedSpriteConfig, SpriteConfig, TransformOptions } from './types.js' + +const CONFIG_EXTENSIONS = new Set(['.js', '.json', '.ts']) +const CONFIG_FIELDS = new Set([ + 'mode', + 'name', + 'description', + 'inputFolder', + 'inputFiles', + 'transform', + 'generatedNotice', +]) +const TRANSFORM_FIELDS = new Set(['removeSize', 'replaceColors', 'addTransition']) +const MODES = new Set([ + 'react@vite', + 'react@webpack', + 'next@app/turbopack', + 'next@app/webpack', + 'next@pages/turbopack', + 'next@pages/webpack', +]) + +export type SpriteConfigSource = { + rootDir: string + configPath: string | null + config: SpriteConfig +} + +export function isSpriteMode(value: unknown): value is SpriteMode { + return typeof value === 'string' && MODES.has(value as SpriteMode) +} + +function getDefaultName(rootDir: string): string { + const rootName = path.basename(rootDir) + const source = rootName === 'svg-sprite' || rootName === 'svg-sprites' + ? path.basename(path.dirname(rootDir)) + : rootName + const name = toKebabCase(source) + + if (!name) throw new Error(`Cannot infer sprite name from directory: ${rootDir}`) + return name +} + +function configError(message: string): Error { + return new Error(`Sprite config: ${message}`) +} + +/** Проверяет единый конфиг независимо от формата исходного файла. */ +export function validateSpriteConfig(value: unknown): asserts value is SpriteConfig { + if (!value || typeof value !== 'object' || Array.isArray(value)) { + throw configError('expected an object.') + } + + const config = value as Record + + if ('output' in config || 'sprites' in config || 'preview' in config) { + throw configError('legacy config fields are no longer supported.') + } + for (const field of Object.keys(config)) { + if (!CONFIG_FIELDS.has(field)) throw configError(`unknown field "${field}".`) + } + if (config.mode !== undefined && !isSpriteMode(config.mode)) { + throw configError(`unsupported "mode": ${String(config.mode)}.`) + } + if (config.name !== undefined && typeof config.name !== 'string') { + throw configError('"name" must be a string.') + } + if (config.description !== undefined && typeof config.description !== 'string') { + throw configError('"description" must be a string.') + } + if ('icons' in config) { + throw configError('"icons" was renamed to "inputFolder".') + } + if (config.inputFolder !== undefined && ( + typeof config.inputFolder !== 'string' || config.inputFolder.trim() === '' + )) { + throw configError('"inputFolder" must be a non-empty string.') + } + if (config.inputFiles !== undefined && ( + !Array.isArray(config.inputFiles) + || config.inputFiles.some((filePath) => typeof filePath !== 'string' || filePath.trim() === '') + )) { + throw configError('"inputFiles" must be an array of non-empty strings.') + } + if (config.transform !== undefined) { + if ( + config.transform === null + || typeof config.transform !== 'object' + || Array.isArray(config.transform) + ) { + throw configError('"transform" must be an object.') + } + + const transform = config.transform as Record + for (const field of Object.keys(transform)) { + if (!TRANSFORM_FIELDS.has(field)) { + throw configError(`unknown field "transform.${field}".`) + } + } + for (const option of ['removeSize', 'replaceColors', 'addTransition']) { + if (transform[option] !== undefined && typeof transform[option] !== 'boolean') { + throw configError(`"transform.${option}" must be a boolean.`) + } + } + } + if (config.generatedNotice !== undefined && typeof config.generatedNotice !== 'boolean') { + throw configError('"generatedNotice" must be a boolean.') + } +} + +function getModuleDefault(value: unknown, configPath: string): unknown { + if (!value || typeof value !== 'object' || !('default' in value)) { + throw new Error( + `Sprite config file must have a default export: ${configPath}\n` + + 'Use: export default defineSpriteConfig({ ... })', + ) + } + return (value as { default: unknown }).default +} + +/** Загружает явно указанный JS, JSON или TS config-файл. */ +export async function loadSpriteConfig(configFile: string): Promise { + const configPath = path.resolve(configFile) + const extension = path.extname(configPath).toLowerCase() + + if (!CONFIG_EXTENSIONS.has(extension)) { + throw new Error(`Unsupported sprite config extension: ${extension || '(none)'}. Supported: .js, .json, .ts.`) + } + if (!fs.existsSync(configPath) || !fs.statSync(configPath).isFile()) { + throw new Error(`Sprite config file does not exist: ${configPath}`) + } + + let config: unknown + if (extension === '.json') { + try { + config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) + } catch (error) { + const reason = error instanceof Error ? error.message : String(error) + throw new Error(`Cannot parse sprite config: ${configPath}\n${reason}`) + } + } else { + const jiti = createJiti(path.dirname(configPath)) + config = getModuleDefault(await jiti.import(configPath), configPath) + } + + validateSpriteConfig(config) + return config +} + +/** Разрешает позиционный путь как config-файл либо config-less корень модуля. */ +export async function resolveSpriteConfigSource(source: string): Promise { + const resolved = path.resolve(source) + + if (!fs.existsSync(resolved)) { + throw new Error(`Sprite config or module directory does not exist: ${resolved}`) + } + + const stats = fs.statSync(resolved) + if (stats.isDirectory()) { + return { rootDir: resolved, configPath: null, config: {} } + } + if (!stats.isFile()) { + throw new Error(`Sprite config path must be a file or directory: ${resolved}`) + } + + return { + rootDir: path.dirname(resolved), + configPath: resolved, + config: await loadSpriteConfig(resolved), + } +} + +function definedEntries(value: T): Partial { + return Object.fromEntries( + Object.entries(value).filter(([, entry]) => entry !== undefined), + ) as Partial +} + +/** Объединяет defaults, config и overrides и разрешает пути от корня модуля. */ +export function resolveSpriteConfig( + rootDir: string, + config: SpriteConfig = {}, + overrides: SpriteConfig = {}, +): ResolvedSpriteConfig { + validateSpriteConfig(config) + validateSpriteConfig(overrides) + + const configValues = definedEntries(config) + const overrideValues = definedEntries(overrides) + const transform: TransformOptions = { + ...config.transform, + ...overrides.transform, + } + const merged: SpriteConfig = { + ...configValues, + ...overrideValues, + transform, + } + + if (!merged.mode) { + throw new Error('Sprite mode is required. Set "mode" in the config or pass it through CLI/API.') + } + + const name = merged.name ?? getDefaultName(rootDir) + validateSpriteName(name) + const inputFiles = (merged.inputFiles ?? []).map((filePath) => path.resolve(rootDir, filePath)) + const defaultInputFolder = path.resolve(rootDir, 'icons') + const inputFolder = merged.inputFolder === undefined + && inputFiles.length > 0 + && !fs.existsSync(defaultInputFolder) + ? null + : path.resolve(rootDir, merged.inputFolder ?? 'icons') + + return { + mode: merged.mode, + name, + description: merged.description, + inputFolder, + inputFiles, + transform: { + removeSize: transform.removeSize ?? true, + replaceColors: transform.replaceColors ?? true, + addTransition: transform.addTransition ?? true, + }, + generatedNotice: merged.generatedNotice ?? true, + } +} diff --git a/src/core/compiled-artifact.ts b/src/core/compiled-artifact.ts new file mode 100644 index 0000000..bc7d9a4 --- /dev/null +++ b/src/core/compiled-artifact.ts @@ -0,0 +1,70 @@ +import { getSpriteShapeId } from '../shape-id.js' +import type { SpriteFormat } from '../types.js' + +export type CompiledIconColor = { + readonly variable: string + readonly fallback: string +} + +export type CompiledIcon = { + readonly name: string + readonly id: string + readonly viewBox: string | null + readonly colors: readonly CompiledIconColor[] +} + +export type CompiledSpriteArtifact = { + readonly format: SpriteFormat + readonly bytes: Uint8Array + readonly icons: readonly CompiledIcon[] +} + +function extractColors(fragment: string): CompiledIconColor[] { + const colors = new Map() + const regex = /var\((--icon-color-\d+),\s*((?:[^()]|\([^()]*\))*)\)/g + let match: RegExpExecArray | null + + while ((match = regex.exec(fragment)) !== null) { + if (!colors.has(match[1])) colors.set(match[1], match[2].trim()) + } + + return [...colors].map(([variable, fallback]) => ({ variable, fallback })) +} + +/** Преобразует compiled SVG в нейтральный artifact для adapter codegen. */ +export function createCompiledSpriteArtifact( + bytes: Uint8Array, + iconNames: readonly string[], + format: SpriteFormat, +): CompiledSpriteArtifact { + const shapes = new Map() + const content = new TextDecoder().decode(bytes) + const shapeRegex = /<(symbol|svg)\b((?=[^>]*\bid="[^"]+")[^>]*)>[\s\S]*?<\/\1>/g + let match: RegExpExecArray | null + + while ((match = shapeRegex.exec(content)) !== null) { + const attributes = match[2] + const id = attributes.match(/\bid="([^"]+)"/)?.[1] + if (!id) continue + + shapes.set(id, { + viewBox: attributes.match(/\bviewBox="([^"]+)"/)?.[1] ?? null, + fragment: match[0], + }) + } + + const icons = iconNames.map((name) => { + const id = getSpriteShapeId(name) + const shape = shapes.get(id) + if (!shape) throw new Error(`Cannot find SVG shape "${id}" for icon "${name}".`) + + return { + name, + id, + viewBox: shape.viewBox, + colors: extractColors(shape.fragment), + } + }) + + return { format, bytes, icons } +} diff --git a/src/core/generated-markers.ts b/src/core/generated-markers.ts new file mode 100644 index 0000000..3637013 --- /dev/null +++ b/src/core/generated-markers.ts @@ -0,0 +1,3 @@ +export const GENERATOR = '@gromlab/svg-sprites' +export const GENERATED_MARKER = '@generated by @gromlab/svg-sprites' +export const GENERATED_NOTICE_MARKER = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ' diff --git a/src/core/mode-adapter.ts b/src/core/mode-adapter.ts new file mode 100644 index 0000000..7416c99 --- /dev/null +++ b/src/core/mode-adapter.ts @@ -0,0 +1,53 @@ +import type { + NextAssetTarget, + NextBundler, + NextRouter, + ReactAssetTarget, + SpriteMode, +} from '../targets/types.js' +import type { ResolvedSpriteConfig, SpriteFolder } from '../types.js' + +export type GeneratedFile = { + readonly path: string + readonly content: string | Uint8Array +} + +export type PreparedSprite = { + readonly folder: SpriteFolder + readonly iconNames: readonly string[] +} + +export type ModeAdapterContext = { + readonly rootDir: string + readonly config: ResolvedSpriteConfig + readonly prepared: PreparedSprite +} + +export type ReactModeResultMetadata = { + readonly target: ReactAssetTarget +} + +export type NextModeResultMetadata = { + readonly target: NextAssetTarget + readonly router: NextRouter + readonly bundler: NextBundler +} + +export type ModeResultMetadata = ReactModeResultMetadata | NextModeResultMetadata + +export type OutputPlan = { + readonly files: readonly GeneratedFile[] + readonly paths: { + readonly generatedDir: '.svg-sprite' + readonly sprite: string + readonly manifest: string + readonly entry: string + } + readonly result: ModeResultMetadata +} + +export interface ModeAdapter { + readonly mode: M + readonly contractVersion: number + generate(context: ModeAdapterContext): Promise +} diff --git a/src/core/naming.ts b/src/core/naming.ts new file mode 100644 index 0000000..1483edd --- /dev/null +++ b/src/core/naming.ts @@ -0,0 +1,17 @@ +/** Преобразует имя каталога в kebab-case имя спрайта. */ +export function toKebabCase(value: string): string { + return value + .normalize('NFKD') + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .replace(/[^a-zA-Z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .toLowerCase() +} + +export function validateSpriteName(name: string): void { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(name)) { + throw new Error( + `Sprite config: "name" must be kebab-case and start with a letter. Received: "${name}".`, + ) + } +} diff --git a/src/core/output-writer.ts b/src/core/output-writer.ts new file mode 100644 index 0000000..d5bbbe4 --- /dev/null +++ b/src/core/output-writer.ts @@ -0,0 +1,297 @@ +import { randomUUID } from 'node:crypto' +import fs from 'node:fs' +import path from 'node:path' +import type { SpriteMode } from '../targets/types.js' +import type { GeneratedFile } from './mode-adapter.js' +import { + GENERATED_MARKER, + GENERATED_NOTICE_MARKER, + GENERATOR, +} from './generated-markers.js' + +const STATE_PATH = '.svg-sprite/state.json' +const PREVIOUS_STATE_PATHS = [ + '.svg-sprite-data/state.json', + 'generated/state.json', +] as const +const LEGACY_STATE_PATH = 'generated/.svg-sprites.manifest.json' +const DATA_ROOT = '.svg-sprite' +const PREVIOUS_DATA_ROOT = '.svg-sprite-data' +const SYSTEM_FILES: readonly GeneratedFile[] = [ + { + path: '.gitignore', + content: `# ${GENERATED_MARKER}. Do not edit.\n/${DATA_ROOT}/\n`, + }, +] + +type OutputState = { + schemaVersion: 1 + generator: typeof GENERATOR + owner: { + mode: SpriteMode + contractVersion: number + } + files: string[] + warning?: string +} + +type LegacyState = { + version: 1 + generator: typeof GENERATOR + files: string[] +} + +function normalizeManagedPath(relativePath: string): string { + const normalized = relativePath.replaceAll('\\', '/') + const parts = normalized.split('/') + + if ( + normalized === '' + || path.posix.isAbsolute(normalized) + || parts.some((part) => part === '' || part === '.' || part === '..') + || normalized === STATE_PATH + || PREVIOUS_STATE_PATHS.includes(normalized as typeof PREVIOUS_STATE_PATHS[number]) + || normalized === LEGACY_STATE_PATH + ) { + throw new Error(`Invalid generated file path: ${relativePath}`) + } + + return normalized +} + +function resolveManagedPath(rootDir: string, relativePath: string): string { + const normalized = normalizeManagedPath(relativePath) + const resolved = path.resolve(rootDir, normalized) + const relative = path.relative(rootDir, resolved) + + if (relative.startsWith('..') || path.isAbsolute(relative)) { + throw new Error(`Invalid generated file path: ${relativePath}`) + } + + return resolved +} + +function resolveInternalPath(rootDir: string, relativePath: string): string { + const resolved = path.resolve(rootDir, relativePath) + const relative = path.relative(rootDir, resolved) + if (relative.startsWith('..') || path.isAbsolute(relative)) { + throw new Error(`Invalid internal output path: ${relativePath}`) + } + return resolved +} + +function assertNoSymlinks(rootDir: string, filePath: string): void { + const relative = path.relative(rootDir, filePath) + let currentPath = rootDir + + for (const segment of relative.split(path.sep)) { + currentPath = path.join(currentPath, segment) + try { + if (fs.lstatSync(currentPath).isSymbolicLink()) { + throw new Error(`Symbolic links are not allowed in generated paths: ${currentPath}`) + } + } catch (error) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') continue + throw error + } + } +} + +function parseJsonFile(filePath: string): unknown { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf-8')) + } catch { + throw new Error(`Cannot parse generated state: ${filePath}`) + } +} + +function readOutputState(statePath: string): OutputState { + const state = parseJsonFile(statePath) + if ( + !state + || typeof state !== 'object' + || !('schemaVersion' in state) + || state.schemaVersion !== 1 + || !('generator' in state) + || state.generator !== GENERATOR + || !('owner' in state) + || !state.owner + || typeof state.owner !== 'object' + || !('mode' in state.owner) + || typeof state.owner.mode !== 'string' + || !('contractVersion' in state.owner) + || typeof state.owner.contractVersion !== 'number' + || !('files' in state) + || !Array.isArray(state.files) + || !state.files.every((file) => typeof file === 'string') + ) { + throw new Error(`Invalid generated state: ${statePath}`) + } + return state as OutputState +} + +function readPreviousFiles(rootDir: string): { + files: string[] + obsoleteStatePath: string | null +} { + for (const relativeStatePath of [STATE_PATH, ...PREVIOUS_STATE_PATHS]) { + const statePath = resolveInternalPath(rootDir, relativeStatePath) + assertNoSymlinks(rootDir, statePath) + if (!fs.existsSync(statePath)) continue + return { + files: readOutputState(statePath).files.map(normalizeManagedPath), + obsoleteStatePath: relativeStatePath === STATE_PATH ? null : statePath, + } + } + + const legacyStatePath = resolveInternalPath(rootDir, LEGACY_STATE_PATH) + assertNoSymlinks(rootDir, legacyStatePath) + if (!fs.existsSync(legacyStatePath)) return { files: [], obsoleteStatePath: null } + + const legacyState = parseJsonFile(legacyStatePath) + if ( + !legacyState + || typeof legacyState !== 'object' + || !('version' in legacyState) + || legacyState.version !== 1 + || !('generator' in legacyState) + || legacyState.generator !== GENERATOR + || !('files' in legacyState) + || !Array.isArray(legacyState.files) + || !legacyState.files.every((file) => typeof file === 'string') + ) { + throw new Error(`Invalid generated state: ${legacyStatePath}`) + } + + return { + files: (legacyState as LegacyState).files.map(normalizeManagedPath), + obsoleteStatePath: legacyStatePath, + } +} + +function removeEmptyStateDirectory(rootDir: string, statePath: string): void { + const directory = path.dirname(statePath) + if (directory === rootDir) return + try { + fs.rmdirSync(directory) + } catch (error) { + if (error instanceof Error && 'code' in error && ( + error.code === 'ENOENT' || error.code === 'ENOTEMPTY' + )) return + throw error + } +} + +function removeEmptyManagedParents(rootDir: string, filePath: string): void { + for (const relativeDataRoot of [DATA_ROOT, PREVIOUS_DATA_ROOT]) { + const dataRoot = resolveInternalPath(rootDir, relativeDataRoot) + let directory = path.dirname(filePath) + const relative = path.relative(dataRoot, directory) + if (relative.startsWith('..') || path.isAbsolute(relative)) continue + + while (directory !== dataRoot) { + try { + fs.rmdirSync(directory) + } catch (error) { + if (error instanceof Error && 'code' in error && ( + error.code === 'ENOENT' || error.code === 'ENOTEMPTY' + )) return + throw error + } + directory = path.dirname(directory) + } + return + } +} + +function hasGeneratedMarker(filePath: string): boolean { + if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return false + const content = fs.readFileSync(filePath, 'utf-8') + return content.includes(GENERATED_MARKER) || content.includes(GENERATED_NOTICE_MARKER) +} + +function writeFileAtomic(rootDir: string, filePath: string, content: string | Uint8Array): void { + assertNoSymlinks(rootDir, filePath) + fs.mkdirSync(path.dirname(filePath), { recursive: true }) + assertNoSymlinks(rootDir, filePath) + + const temporaryPath = path.join( + path.dirname(filePath), + `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`, + ) + + try { + fs.writeFileSync(temporaryPath, content, { flag: 'wx' }) + fs.renameSync(temporaryPath, filePath) + } finally { + if (fs.existsSync(temporaryPath)) fs.unlinkSync(temporaryPath) + } +} + +/** Применяет mode output plan и последним записывает ownership state. */ +export function writeOutputPlan( + rootDir: string, + mode: SpriteMode, + contractVersion: number, + files: readonly GeneratedFile[], + generatedNotice: boolean, +): void { + const normalizedFiles = [...SYSTEM_FILES, ...files].map((file) => ({ + ...file, + path: normalizeManagedPath(file.path), + })) + const nextFiles = normalizedFiles.map((file) => file.path) + + if (new Set(nextFiles).size !== nextFiles.length) { + throw new Error(`Mode "${mode}" produced duplicate generated file paths.`) + } + + const previous = readPreviousFiles(rootDir) + const obsoleteFiles: string[] = [] + + for (const file of normalizedFiles) { + const filePath = resolveManagedPath(rootDir, file.path) + assertNoSymlinks(rootDir, filePath) + if (fs.existsSync(filePath) && !hasGeneratedMarker(filePath)) { + throw new Error( + `Refusing to overwrite a user file: ${filePath}\n` + + 'Move the file or choose another sprite directory.', + ) + } + } + + for (const relativePath of previous.files) { + if (nextFiles.includes(relativePath)) continue + const filePath = resolveManagedPath(rootDir, relativePath) + assertNoSymlinks(rootDir, filePath) + + if (fs.existsSync(filePath)) { + if (!hasGeneratedMarker(filePath)) { + throw new Error(`Refusing to delete a user file: ${filePath}`) + } + obsoleteFiles.push(filePath) + } + } + + for (const file of normalizedFiles) { + writeFileAtomic(rootDir, resolveManagedPath(rootDir, file.path), file.content) + } + for (const filePath of obsoleteFiles) fs.unlinkSync(filePath) + for (const filePath of obsoleteFiles) removeEmptyManagedParents(rootDir, filePath) + if (previous.obsoleteStatePath && fs.existsSync(previous.obsoleteStatePath)) { + fs.unlinkSync(previous.obsoleteStatePath) + removeEmptyStateDirectory(rootDir, previous.obsoleteStatePath) + } + + const state: OutputState = { + schemaVersion: 1, + generator: GENERATOR, + owner: { mode, contractVersion }, + files: nextFiles, + ...(generatedNotice && { + warning: 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную: изменения будут перезаписаны.', + }), + } + const statePath = resolveInternalPath(rootDir, STATE_PATH) + writeFileAtomic(rootDir, statePath, `${JSON.stringify(state, null, 2)}\n`) +} diff --git a/src/core/prepare-sprite.ts b/src/core/prepare-sprite.ts new file mode 100644 index 0000000..a63046a --- /dev/null +++ b/src/core/prepare-sprite.ts @@ -0,0 +1,38 @@ +import path from 'node:path' +import { resolveSpriteSources } from '../scanner.js' +import { getSpriteShapeId } from '../shape-id.js' +import type { ResolvedSpriteConfig } from '../types.js' +import type { PreparedSprite } from './mode-adapter.js' + +function validateIconIds(iconNames: readonly string[]): void { + const namesById = new Map() + + for (const iconName of iconNames) { + const id = getSpriteShapeId(iconName) + const existingName = namesById.get(id) + + if (existingName) { + throw new Error( + `Icons "${existingName}" and "${iconName}" produce the same SVG id "${id}". Rename one of the files.`, + ) + } + + namesById.set(id, iconName) + } +} + +/** Подготавливает mode-neutral набор исходников. */ +export function prepareSprite(config: ResolvedSpriteConfig): PreparedSprite { + const folder = resolveSpriteSources({ + name: config.name, + format: 'stack', + inputFolder: config.inputFolder, + inputFiles: config.inputFiles, + }) + const iconNames = folder.files + .map((filePath) => path.basename(filePath, '.svg')) + .sort() + + validateIconIds(iconNames) + return { folder, iconNames } +} diff --git a/src/core/result.ts b/src/core/result.ts new file mode 100644 index 0000000..eeee287 --- /dev/null +++ b/src/core/result.ts @@ -0,0 +1,15 @@ +import type { SpriteAssetTarget, SpriteMode } from '../targets/types.js' + +export type SpriteGenerationBaseResult< + TMode extends SpriteMode = SpriteMode, + TTarget extends SpriteAssetTarget = SpriteAssetTarget, +> = { + name: string + rootDir: string + generatedDir: string + spritePath: string + manifestPath: string + iconCount: number + mode: TMode + target: TTarget +} diff --git a/src/generate.ts b/src/generate.ts new file mode 100644 index 0000000..3235abb --- /dev/null +++ b/src/generate.ts @@ -0,0 +1,65 @@ +import path from 'node:path' +import { + resolveSpriteConfig, + resolveSpriteConfigSource, +} from './config.js' +import type { ModeResultMetadata } from './core/mode-adapter.js' +import { writeOutputPlan } from './core/output-writer.js' +import { prepareSprite } from './core/prepare-sprite.js' +import type { SpriteGenerationBaseResult } from './core/result.js' +import { log } from './logger.js' +import { getModeAdapter } from './mode-registry.js' +import type { SpriteConfig } from './types.js' + +export type SpriteGenerationResult = SpriteGenerationBaseResult & ModeResultMetadata + +/** Генерирует один output через adapter разрешённого exact mode. */ +export async function generateSprite( + source: string, + overrides: SpriteConfig = {}, +): Promise { + const resolvedSource = await resolveSpriteConfigSource(source) + const config = resolveSpriteConfig( + resolvedSource.rootDir, + resolvedSource.config, + overrides, + ) + const adapter = getModeAdapter(config.mode) + const prepared = prepareSprite(config) + const plan = await adapter.generate({ + rootDir: resolvedSource.rootDir, + config, + prepared, + }) + const plannedPaths = new Set(plan.files.map((file) => file.path.replaceAll('\\', '/'))) + + for (const requiredPath of [plan.paths.entry, plan.paths.sprite, plan.paths.manifest]) { + if (!plannedPaths.has(requiredPath)) { + throw new Error(`Mode "${config.mode}" result path is missing from its output plan: ${requiredPath}`) + } + } + + writeOutputPlan( + resolvedSource.rootDir, + adapter.mode, + adapter.contractVersion, + plan.files, + config.generatedNotice, + ) + + const generatedDir = path.resolve(resolvedSource.rootDir, plan.paths.generatedDir) + const iconLabel = prepared.iconNames.length === 1 ? 'icon' : 'icons' + log.success(`✓ ${config.name} · ${prepared.iconNames.length} ${iconLabel} · ${config.mode}`) + log.detail(` → ${path.relative(process.cwd(), generatedDir)}`) + + return { + name: config.name, + rootDir: resolvedSource.rootDir, + generatedDir, + spritePath: path.resolve(resolvedSource.rootDir, plan.paths.sprite), + manifestPath: path.resolve(resolvedSource.rootDir, plan.paths.manifest), + iconCount: prepared.iconNames.length, + mode: config.mode, + ...plan.result, + } +} diff --git a/src/index.ts b/src/index.ts index 08109f6..f02f376 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,33 +1,36 @@ -import type { SvgSpritesConfig } from './types.js' -import type { NextSpriteConfig } from './modes/next/types.js' -import type { ReactSpriteConfig } from './modes/react/types.js' +import type { NextSpriteConfig } from './api/next.js' +import type { ReactSpriteConfig } from './api/react.js' +import type { SpriteConfig } from './types.js' -export { generateLegacy } from './modes/legacy/generate.js' -export { resolveSprites, resolveSpriteEntry } from './scanner.js' +export { + isSpriteMode, + loadSpriteConfig, + resolveSpriteConfig, + resolveSpriteConfigSource, + validateSpriteConfig, +} from './config.js' +export { generateSprite } from './generate.js' +export type { SpriteGenerationResult } from './generate.js' +export { generateNextSprite } from './api/next.js' +export { generateReactSprite } from './api/react.js' export { compileSprite, compileSpriteContent } from './compiler.js' export type { CompileSpriteOptions } from './compiler.js' export { createShapeTransform } from './transforms.js' -export { generatePreview } from './preview.js' -export { loadLegacyConfig } from './modes/legacy/config.js' -export { generateNextSprite } from './modes/next/index.js' -export { - generateReactSprite, - loadReactSpriteConfig, -} from './modes/react/index.js' + export type { NextAssetTarget, NextBundler, NextRouter, ReactAssetTarget, + ReactSpriteMode, SpriteAssetTarget, + SpriteMode, ViteAssetTarget, WebpackAssetTarget, } from './targets/types.js' - export type { - SvgSpritesConfig, - SpriteEntry, - SpriteResult, + ResolvedSpriteConfig, + SpriteConfig, SpriteFolder, SpriteFormat, TransformOptions, @@ -36,24 +39,24 @@ export type { NextSpriteConfig, NextSpriteGenerationOptions, NextSpriteGenerationResult, -} from './modes/next/types.js' +} from './api/next.js' export type { ReactSpriteConfig, ReactSpriteGenerationResult, ResolvedReactSpriteConfig, -} from './modes/react/types.js' +} from './api/react.js' -/** Хелпер для типизации legacy-конфига. */ -export function defineLegacyConfig(config: SvgSpritesConfig): SvgSpritesConfig { +/** Хелпер для типизации единого sprite-конфига. */ +export function defineSpriteConfig(config: SpriteConfig): SpriteConfig { return config } -/** Хелпер для типизации локального React-конфига. */ +/** @deprecated Используйте defineSpriteConfig. */ export function defineReactSpriteConfig(config: ReactSpriteConfig): ReactSpriteConfig { return config } -/** Хелпер для типизации локального Next.js-конфига. */ +/** @deprecated Используйте defineSpriteConfig. */ export function defineNextSpriteConfig(config: NextSpriteConfig): NextSpriteConfig { return config } diff --git a/src/mode-registry.ts b/src/mode-registry.ts new file mode 100644 index 0000000..61c1f51 --- /dev/null +++ b/src/mode-registry.ts @@ -0,0 +1,21 @@ +import type { ModeAdapter } from './core/mode-adapter.js' +import { nextAppTurbopackAdapter } from './modes/next-app-turbopack/adapter.js' +import { nextAppWebpackAdapter } from './modes/next-app-webpack/adapter.js' +import { nextPagesTurbopackAdapter } from './modes/next-pages-turbopack/adapter.js' +import { nextPagesWebpackAdapter } from './modes/next-pages-webpack/adapter.js' +import { reactViteAdapter } from './modes/react-vite/adapter.js' +import { reactWebpackAdapter } from './modes/react-webpack/adapter.js' +import type { SpriteMode } from './targets/types.js' + +const modeRegistry: Record = { + 'react@vite': reactViteAdapter, + 'react@webpack': reactWebpackAdapter, + 'next@app/turbopack': nextAppTurbopackAdapter, + 'next@app/webpack': nextAppWebpackAdapter, + 'next@pages/turbopack': nextPagesTurbopackAdapter, + 'next@pages/webpack': nextPagesWebpackAdapter, +} + +export function getModeAdapter(mode: SpriteMode): ModeAdapter { + return modeRegistry[mode] +} diff --git a/src/modes/legacy/config.ts b/src/modes/legacy/config.ts deleted file mode 100644 index 4b5a602..0000000 --- a/src/modes/legacy/config.ts +++ /dev/null @@ -1,74 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { createJiti } from 'jiti' -import type { SvgSpritesConfig } from '../../types.js' - -const CONFIG_NAME = 'svg-sprites.config' - -/** Загружает legacy-конфиг из указанной директории. */ -export async function loadLegacyConfig(cwd: string = process.cwd()): Promise { - const configPath = path.join(cwd, `${CONFIG_NAME}.ts`) - - if (!fs.existsSync(configPath)) { - throw new Error( - `Config file not found: ${configPath}\n` + - `Create a ${CONFIG_NAME}.ts file in the project root.`, - ) - } - - const jiti = createJiti(cwd) - const mod = await jiti.import(configPath) as { default?: SvgSpritesConfig } - const config = mod.default - - if (!config) { - throw new Error( - `Config file must have a default export: ${configPath}\n` + - 'Use: export default defineLegacyConfig({ ... })', - ) - } - - validateLegacyConfig(config) - return { - ...config, - output: path.resolve(cwd, config.output), - sprites: config.sprites.map((sprite) => ({ - ...sprite, - input: Array.isArray(sprite.input) - ? sprite.input.map((filePath) => path.resolve(cwd, filePath)) - : path.resolve(cwd, sprite.input), - })), - } -} - -/** Валидирует legacy-конфиг. */ -export function validateLegacyConfig(config: SvgSpritesConfig): void { - if (!config.output) { - throw new Error('Config: "output" is required.') - } - - if (!config.sprites || config.sprites.length === 0) { - throw new Error('Config: "sprites" must be a non-empty array.') - } - - for (const sprite of config.sprites) { - if ('mode' in sprite) { - throw new Error( - `Config: sprite "${sprite.name}" uses deprecated "mode". Use "format" instead.`, - ) - } - - if (!sprite.name) { - throw new Error('Config: each sprite must have a "name".') - } - - if (!sprite.input) { - throw new Error(`Config: sprite "${sprite.name}" must have an "input".`) - } - - if (sprite.format && sprite.format !== 'stack' && sprite.format !== 'symbol') { - throw new Error( - `Config: sprite "${sprite.name}" has invalid format "${sprite.format}". Supported: stack, symbol.`, - ) - } - } -} diff --git a/src/modes/legacy/generate.ts b/src/modes/legacy/generate.ts deleted file mode 100644 index 7833fed..0000000 --- a/src/modes/legacy/generate.ts +++ /dev/null @@ -1,52 +0,0 @@ -import path from 'node:path' -import { compileSprite } from '../../compiler.js' -import { log } from '../../logger.js' -import { generatePreview } from '../../preview.js' -import { resolveSprites } from '../../scanner.js' -import type { SpriteResult, SvgSpritesConfig } from '../../types.js' - -/** Генерирует SVG-спрайты через legacy pipeline. */ -export async function generateLegacy(config: SvgSpritesConfig): Promise { - const { - output, - preview = true, - transform = {}, - sprites, - } = config - const outputDir = path.resolve(output) - - log.title('Resolving legacy sprites...') - - const folders = resolveSprites(sprites) - - if (folders.length === 0) { - log.warn('No sprites to generate.') - return [] - } - - log.info(`Found ${folders.length} sprite(s)\n`) - - const results: SpriteResult[] = [] - - for (const folder of folders) { - const spritePath = await compileSprite(folder, outputDir, transform) - log.success(` [${folder.format}] ${folder.name} → ${path.relative(process.cwd(), spritePath)} (${folder.files.length} icons)`) - - results.push({ - name: folder.name, - format: folder.format, - spritePath, - iconCount: folder.files.length, - }) - } - - if (preview) { - const previewPath = generatePreview(results, outputDir) - log.success(`\n [preview] → ${path.relative(process.cwd(), previewPath)}`) - } - - console.log('') - log.success(`Done! Generated ${results.length} sprite(s).`) - - return results -} diff --git a/src/modes/legacy/index.ts b/src/modes/legacy/index.ts deleted file mode 100644 index 7d5a636..0000000 --- a/src/modes/legacy/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { loadLegacyConfig, validateLegacyConfig } from './config.js' -export { generateLegacy } from './generate.js' diff --git a/src/modes/next-app-turbopack/adapter.ts b/src/modes/next-app-turbopack/adapter.ts new file mode 100644 index 0000000..c059912 --- /dev/null +++ b/src/modes/next-app-turbopack/adapter.ts @@ -0,0 +1,26 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nextAppTurbopackAdapter: ModeAdapter<'next@app/turbopack'> = { + mode: 'next@app/turbopack', + contractVersion: 5, + + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: true, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'next@app/turbopack', router: 'app', bundler: 'turbopack' }, + } + }, +} diff --git a/src/modes/next-app-turbopack/output.ts b/src/modes/next-app-turbopack/output.ts new file mode 100644 index 0000000..7bea198 --- /dev/null +++ b/src/modes/next-app-turbopack/output.ts @@ -0,0 +1,170 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'next@app/turbopack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function comment(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [header(config.generatedNotice), '', ...comment(config), `export const ${prefix}IconNames = ${JSON.stringify(names, null, 2)}`, '', `export const ${prefix}IconIds = ${JSON.stringify(ids, null, 2)}`, ''].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { jsx } from 'react/jsx-runtime'", + "import styles from './react-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `export const ${componentName} = (props) => {`, + ' const { icon, wrapped, className, ...rest } = props', + ` const href = spriteUrl + '#' + ${ids}[icon]`, + ' if (wrapped) {', + " return jsx('span', {", + ' ...rest,', + " className: [styles.wrap, className].filter(Boolean).join(' '),", + " children: jsx('svg', { children: jsx('use', { href }) }),", + ' })', + ' }', + " return jsx('svg', {", + ' ...rest,', + " className: [styles.root, className].filter(Boolean).join(' '),", + " children: jsx('use', { href }),", + ' })', + '}', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...comment(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${typeName} = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const styleName = `${componentName}Style` + const propsName = `${componentName}Props` + return [header(config.generatedNotice), + "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", + `import type { ${typeName} } from '../icon-data.js'`, + '', + `export type ${styleName} = CSSProperties & Partial>`, + `type ${componentName}BaseProps = { icon: ${typeName} }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${styleName} }`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${styleName} }`, + `export type ${propsName} = ${componentName}SvgProps | ${componentName}WrappedProps`, + `export declare const ${componentName}: (props: ${propsName}) => ReactElement`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './react/react-component.js'`, + `export type { ${componentName}Props, ${componentName}Style } from './react/react-component.js'`, + `export { ${names} } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), `export { ${pascal(config.name)}Icon } from './react/react-component.js'`, `export { ${camel(config.name)}IconNames } from './icon-data.js'`, ''].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + componentName: `${pascal(config.name)}Icon`, + mode: MODE, + target: MODE, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/next-app-webpack/adapter.ts b/src/modes/next-app-webpack/adapter.ts new file mode 100644 index 0000000..9d20de5 --- /dev/null +++ b/src/modes/next-app-webpack/adapter.ts @@ -0,0 +1,18 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nextAppWebpackAdapter: ModeAdapter<'next@app/webpack'> = { + mode: 'next@app/webpack', + contractVersion: 5, + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + return { + files: generateOutputFiles(context.config, artifact), + paths: { generatedDir: '.svg-sprite', sprite: '.svg-sprite/sprite.svg', manifest: '.svg-sprite/svg-sprite.manifest.js', entry: '.svg-sprite/index.js' }, + result: { target: 'next@app/webpack', router: 'app', bundler: 'webpack' }, + } + }, +} diff --git a/src/modes/next-app-webpack/output.ts b/src/modes/next-app-webpack/output.ts new file mode 100644 index 0000000..cebb996 --- /dev/null +++ b/src/modes/next-app-webpack/output.ts @@ -0,0 +1,88 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'next@app/webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' +const pascal = (value: string) => value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +const camel = (value: string) => value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +const header = (enabled: boolean) => enabled ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` : `/* ${GENERATED_MARKER}. Do not edit. */` + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [header(config.generatedNotice), '', ...docs(config), `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, '', `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, ''].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const name = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), "import { jsx } from 'react/jsx-runtime'", "import styles from './react-component.module.css'", `import { ${ids} } from '../icon-data.js'`, '', "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", '', `export const ${name} = (props) => {`, ' const { icon, wrapped, className, ...rest } = props', ` const href = spriteUrl + '#' + ${ids}[icon]`, ' if (wrapped) {', " return jsx('span', {", ' ...rest,', " className: [styles.wrap, className].filter(Boolean).join(' '),", " children: jsx('svg', { children: jsx('use', { href }) }),", ' })', ' }', " return jsx('svg', {", ' ...rest,', " className: [styles.root, className].filter(Boolean).join(' '),", " children: jsx('use', { href }),", ' })', '}', ''].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), '', ...docs(config), `export declare const ${names}: readonly [`, ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), ']', `export type ${typeName} = typeof ${names}[number]`, `export declare const ${ids}: {`, ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), '}', ''].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const styleName = `${componentName}Style` + const propsName = `${componentName}Props` + return [header(config.generatedNotice), "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", `import type { ${typeName} } from '../icon-data.js'`, '', `export type ${styleName} = CSSProperties & Partial>`, `type ${componentName}BaseProps = { icon: ${typeName} }`, `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${styleName} }`, `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${styleName} }`, `export type ${propsName} = ${componentName}SvgProps | ${componentName}WrappedProps`, `export declare const ${componentName}: (props: ${propsName}) => ReactElement`, ''].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [header(config.generatedNotice), `export { ${componentName} } from './react/react-component.js'`, `export type { ${componentName}Props, ${componentName}Style } from './react/react-component.js'`, `export { ${names} } from './icon-data.js'`, `export type { ${componentName}Name } from './icon-data.js'`, ''].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), `export { ${pascal(config.name)}Icon } from './react/react-component.js'`, `export { ${camel(config.name)}IconNames } from './icon-data.js'`, ''].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { schemaVersion: 1, generator: '@gromlab/svg-sprites', name: config.name, ...(config.description === undefined ? {} : { description: config.description }), componentName: `${pascal(config.name)}Icon`, mode: MODE, target: MODE, format: artifact.format, iconCount: artifact.icons.length, spriteUrl: '__SPRITE_URL__', icons: artifact.icons } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/next-pages-turbopack/adapter.ts b/src/modes/next-pages-turbopack/adapter.ts new file mode 100644 index 0000000..8f02f7e --- /dev/null +++ b/src/modes/next-pages-turbopack/adapter.ts @@ -0,0 +1,18 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nextPagesTurbopackAdapter: ModeAdapter<'next@pages/turbopack'> = { + mode: 'next@pages/turbopack', + contractVersion: 5, + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + return { + files: generateOutputFiles(context.config, artifact), + paths: { generatedDir: '.svg-sprite', sprite: '.svg-sprite/sprite.svg', manifest: '.svg-sprite/svg-sprite.manifest.js', entry: '.svg-sprite/index.js' }, + result: { target: 'next@pages/turbopack', router: 'pages', bundler: 'turbopack' }, + } + }, +} diff --git a/src/modes/next-pages-turbopack/output.ts b/src/modes/next-pages-turbopack/output.ts new file mode 100644 index 0000000..816f987 --- /dev/null +++ b/src/modes/next-pages-turbopack/output.ts @@ -0,0 +1,88 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'next@pages/turbopack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' +const pascal = (value: string) => value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +const camel = (value: string) => value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +const header = (enabled: boolean) => enabled ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` : `/* ${GENERATED_MARKER}. Do not edit. */` + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [header(config.generatedNotice), '', ...docs(config), `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, '', `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, ''].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const name = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), "import { jsx } from 'react/jsx-runtime'", "import styles from './react-component.module.css'", `import { ${ids} } from '../icon-data.js'`, '', "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", '', `export const ${name} = (props) => {`, ' const { icon, wrapped, className, ...rest } = props', ` const href = spriteUrl + '#' + ${ids}[icon]`, ' if (wrapped) {', " return jsx('span', {", ' ...rest,', " className: [styles.wrap, className].filter(Boolean).join(' '),", " children: jsx('svg', { children: jsx('use', { href }) }),", ' })', ' }', " return jsx('svg', {", ' ...rest,', " className: [styles.root, className].filter(Boolean).join(' '),", " children: jsx('use', { href }),", ' })', '}', ''].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), '', ...docs(config), `export declare const ${names}: readonly [`, ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), ']', `export type ${typeName} = typeof ${names}[number]`, `export declare const ${ids}: {`, ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), '}', ''].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const styleName = `${componentName}Style` + const propsName = `${componentName}Props` + return [header(config.generatedNotice), "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", `import type { ${typeName} } from '../icon-data.js'`, '', `export type ${styleName} = CSSProperties & Partial>`, `type ${componentName}BaseProps = { icon: ${typeName} }`, `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${styleName} }`, `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${styleName} }`, `export type ${propsName} = ${componentName}SvgProps | ${componentName}WrappedProps`, `export declare const ${componentName}: (props: ${propsName}) => ReactElement`, ''].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [header(config.generatedNotice), `export { ${componentName} } from './react/react-component.js'`, `export type { ${componentName}Props, ${componentName}Style } from './react/react-component.js'`, `export { ${names} } from './icon-data.js'`, `export type { ${componentName}Name } from './icon-data.js'`, ''].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), `export { ${pascal(config.name)}Icon } from './react/react-component.js'`, `export { ${camel(config.name)}IconNames } from './icon-data.js'`, ''].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { schemaVersion: 1, generator: '@gromlab/svg-sprites', name: config.name, ...(config.description === undefined ? {} : { description: config.description }), componentName: `${pascal(config.name)}Icon`, mode: MODE, target: MODE, format: artifact.format, iconCount: artifact.icons.length, spriteUrl: '__SPRITE_URL__', icons: artifact.icons } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/next-pages-webpack/adapter.ts b/src/modes/next-pages-webpack/adapter.ts new file mode 100644 index 0000000..a09142c --- /dev/null +++ b/src/modes/next-pages-webpack/adapter.ts @@ -0,0 +1,18 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const nextPagesWebpackAdapter: ModeAdapter<'next@pages/webpack'> = { + mode: 'next@pages/webpack', + contractVersion: 5, + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + return { + files: generateOutputFiles(context.config, artifact), + paths: { generatedDir: '.svg-sprite', sprite: '.svg-sprite/sprite.svg', manifest: '.svg-sprite/svg-sprite.manifest.js', entry: '.svg-sprite/index.js' }, + result: { target: 'next@pages/webpack', router: 'pages', bundler: 'webpack' }, + } + }, +} diff --git a/src/modes/next-pages-webpack/output.ts b/src/modes/next-pages-webpack/output.ts new file mode 100644 index 0000000..431cfeb --- /dev/null +++ b/src/modes/next-pages-webpack/output.ts @@ -0,0 +1,88 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'next@pages/webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' +const pascal = (value: string) => value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +const camel = (value: string) => value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +const header = (enabled: boolean) => enabled ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` : `/* ${GENERATED_MARKER}. Do not edit. */` + +function markedSvg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) : `\n${content}` +} + +function docs(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [header(config.generatedNotice), '', ...docs(config), `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, '', `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, ''].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const name = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), "import { jsx } from 'react/jsx-runtime'", "import styles from './react-component.module.css'", `import { ${ids} } from '../icon-data.js'`, '', "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", '', `export const ${name} = (props) => {`, ' const { icon, wrapped, className, ...rest } = props', ` const href = spriteUrl + '#' + ${ids}[icon]`, ' if (wrapped) {', " return jsx('span', {", ' ...rest,', " className: [styles.wrap, className].filter(Boolean).join(' '),", " children: jsx('svg', { children: jsx('use', { href }) }),", ' })', ' }', " return jsx('svg', {", ' ...rest,', " className: [styles.root, className].filter(Boolean).join(' '),", " children: jsx('use', { href }),", ' })', '}', ''].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [header(config.generatedNotice), '', ...docs(config), `export declare const ${names}: readonly [`, ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), ']', `export type ${typeName} = typeof ${names}[number]`, `export declare const ${ids}: {`, ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), '}', ''].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const styleName = `${componentName}Style` + const propsName = `${componentName}Props` + return [header(config.generatedNotice), "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", `import type { ${typeName} } from '../icon-data.js'`, '', `export type ${styleName} = CSSProperties & Partial>`, `type ${componentName}BaseProps = { icon: ${typeName} }`, `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${styleName} }`, `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${styleName} }`, `export type ${propsName} = ${componentName}SvgProps | ${componentName}WrappedProps`, `export declare const ${componentName}: (props: ${propsName}) => ReactElement`, ''].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [header(config.generatedNotice), `export { ${componentName} } from './react/react-component.js'`, `export type { ${componentName}Props, ${componentName}Style } from './react/react-component.js'`, `export { ${names} } from './icon-data.js'`, `export type { ${componentName}Name } from './icon-data.js'`, ''].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), `export { ${pascal(config.name)}Icon } from './react/react-component.js'`, `export { ${camel(config.name)}IconNames } from './icon-data.js'`, ''].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { schemaVersion: 1, generator: '@gromlab/svg-sprites', name: config.name, ...(config.description === undefined ? {} : { description: config.description }), componentName: `${pascal(config.name)}Icon`, mode: MODE, target: MODE, format: artifact.format, iconCount: artifact.icons.length, spriteUrl: '__SPRITE_URL__', icons: artifact.icons } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/next/generate.ts b/src/modes/next/generate.ts deleted file mode 100644 index d1422cb..0000000 --- a/src/modes/next/generate.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { NextAssetTarget } from '../../targets/types.js' -import { generateSpriteModule } from '../react/module-generator.js' -import type { - NextSpriteGenerationOptions, - NextSpriteGenerationResult, -} from './types.js' - -/** Генерирует Next.js sprite-модуль для явно выбранных роутера и сборщика. */ -export async function generateNextSprite( - root: string, - options: NextSpriteGenerationOptions, -): Promise { - if (!options || (options.router !== 'app' && options.router !== 'pages')) { - throw new Error(`Unsupported Next.js router: ${String(options?.router)}`) - } - if (options.bundler !== 'turbopack' && options.bundler !== 'webpack') { - throw new Error(`Unsupported Next.js bundler: ${String(options.bundler)}`) - } - - const { router, bundler } = options - const target: NextAssetTarget = `next@${router}/${bundler}` - const result = await generateSpriteModule(root, target, { - mode: target, - rootViewBox: true, - }) - - return { - ...result, - router, - bundler, - } -} diff --git a/src/modes/next/index.ts b/src/modes/next/index.ts deleted file mode 100644 index f94e9c9..0000000 --- a/src/modes/next/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { generateNextSprite } from './generate.js' -export type { - NextSpriteConfig, - NextSpriteGenerationOptions, - NextSpriteGenerationResult, -} from './types.js' diff --git a/src/modes/next/types.ts b/src/modes/next/types.ts deleted file mode 100644 index 2185a6b..0000000 --- a/src/modes/next/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { - NextAssetTarget, - NextBundler, - NextRouter, -} from '../../targets/types.js' -import type { - ReactSpriteConfig, - SpriteModuleGenerationResult, -} from '../react/types.js' - -/** Конфигурация Next.js sprite-модуля. Совпадает с React-конфигурацией. */ -export type NextSpriteConfig = ReactSpriteConfig - -export type NextSpriteGenerationOptions = { - router: NextRouter - bundler: NextBundler -} - -export type NextSpriteGenerationResult = SpriteModuleGenerationResult & { - router: NextRouter - bundler: NextBundler -} diff --git a/src/modes/react-vite/adapter.ts b/src/modes/react-vite/adapter.ts new file mode 100644 index 0000000..976f41a --- /dev/null +++ b/src/modes/react-vite/adapter.ts @@ -0,0 +1,29 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const reactViteAdapter: ModeAdapter<'react@vite'> = { + mode: 'react@vite', + contractVersion: 5, + + async generate(context) { + const bytes = await compileSpriteContent( + context.prepared.folder, + context.config.transform, + { rootViewBox: false }, + ) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'vite' }, + } + }, +} diff --git a/src/modes/react-vite/output.ts b/src/modes/react-vite/output.ts new file mode 100644 index 0000000..c63a1af --- /dev/null +++ b/src/modes/react-vite/output.ts @@ -0,0 +1,268 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'react@vite' +const TARGET = 'vite' +const OUTPUT_DIR = '.svg-sprite' +const GENERATED_NOTICE = [ + '----------------------------------------------------------------------', + '## АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ ##', + '## ##', + '## Не редактируйте вручную: изменения будут перезаписаны. ##', + '## Для изменений перегенерируйте SVG-спрайт. ##', + '## ##', + '## Генератор: @gromlab/svg-sprites ##', + '## Репозиторий: https://github.com/gromlab-ru/svg-sprites ##', + '----------------------------------------------------------------------', +] + +function toPascalCase(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function toCamelCase(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function blockHeader(enabled: boolean): string { + if (!enabled) return `/* ${GENERATED_MARKER}. Do not edit. */` + return ['/*', ...GENERATED_NOTICE.map((line) => ` * ${line}`), ' */'].join('\n') +} + +function svgHeader(enabled: boolean): string { + if (!enabled) return `` + return [''].join('\n') +} + +function formatSvg(content: string): string { + let depth = 0 + const lines = content.replace(/>\n<').split('\n') + const formatted = lines.map((line) => { + const trimmed = line.trim() + const tags = trimmed.match(/<[^>]+>/g) ?? [] + const startsWithClosingTag = trimmed.startsWith('')) { + depth += 1 + } + } + return formattedLine + }) + return `${formatted.join('\n')}\n` +} + +function markedSvg(bytes: Uint8Array, notice: boolean): string { + const svg = formatSvg(new TextDecoder().decode(bytes)) + const header = svgHeader(notice) + return svg.startsWith(')\s*/, `$1\n${header}\n`) + : `${header}\n${svg}` +} + +function descriptionComment(name: string, description?: string): string[] { + const text = description ?? `Имена иконок SVG-спрайта «${name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function generateIconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const camelName = toCamelCase(config.name) + const names = artifact.icons.map((icon) => icon.name) + const ids = Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])) + return [ + blockHeader(config.generatedNotice), + '', + ...descriptionComment(config.name, config.description), + `export const ${camelName}IconNames = ${JSON.stringify(names, null, 2)}`, + '', + `export const ${camelName}IconIds = ${JSON.stringify(ids, null, 2)}`, + '', + ].join('\n') +} + +function generateComponent(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + "import { jsx } from 'react/jsx-runtime'", + "import styles from './react-component.module.css'", + `import { ${camelName}IconIds } from '../icon-data.js'`, + "import spriteUrl from '../sprite.svg?no-inline'", + '', + `/** Иконка из SVG-спрайта «${config.name}». */`, + `export const ${pascalName}Icon = (props) => {`, + ' const { icon, wrapped, className, ...rest } = props', + ` const href = spriteUrl + '#' + ${camelName}IconIds[icon]`, + '', + ' if (wrapped) {', + " return jsx('span', {", + ' ...rest,', + " className: [styles.wrap, className].filter(Boolean).join(' '),", + " children: jsx('svg', { children: jsx('use', { href }) }),", + ' })', + ' }', + '', + " return jsx('svg', {", + ' ...rest,', + " className: [styles.root, className].filter(Boolean).join(' '),", + " children: jsx('use', { href }),", + ' })', + '}', + '', + ].join('\n') +} + +function generateIconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + const tuple = artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`).join('\n') + const ids = artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`).join('\n') + return [ + blockHeader(config.generatedNotice), + '', + ...descriptionComment(config.name, config.description), + `export declare const ${camelName}IconNames: readonly [`, + tuple, + ']', + '', + `export type ${pascalName}IconName = typeof ${camelName}IconNames[number]`, + `export declare const ${camelName}IconIds: {`, + ids, + '}', + '', + ].join('\n') +} + +function generateComponentDeclarations(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + return [ + blockHeader(config.generatedNotice), + "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", + `import type { ${pascalName}IconName } from '../icon-data.js'`, + '', + `export type ${pascalName}IconStyle = CSSProperties & Partial>`, + '', + `type ${pascalName}IconBaseProps = { icon: ${pascalName}IconName }`, + `type ${pascalName}IconSvgProps = ${pascalName}IconBaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${pascalName}IconStyle }`, + `type ${pascalName}IconWrappedProps = ${pascalName}IconBaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${pascalName}IconStyle }`, + `export type ${pascalName}IconProps = ${pascalName}IconSvgProps | ${pascalName}IconWrappedProps`, + '', + `export declare const ${pascalName}Icon: (props: ${pascalName}IconProps) => ReactElement`, + '', + ].join('\n') +} + +function generateIndexDeclarations(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + `export { ${pascalName}Icon } from './react/react-component.js'`, + `export type { ${pascalName}IconProps, ${pascalName}IconStyle } from './react/react-component.js'`, + `export { ${camelName}IconNames } from './icon-data.js'`, + `export type { ${pascalName}IconName } from './icon-data.js'`, + '', + ].join('\n') +} + +function generateIndex(config: ResolvedSpriteConfig): string { + const pascalName = toPascalCase(config.name) + const camelName = toCamelCase(config.name) + return [ + blockHeader(config.generatedNotice), + `export { ${pascalName}Icon } from './react/react-component.js'`, + `export { ${camelName}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function generateStyles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [ + blockHeader(config.generatedNotice), + '', + '.root {', + ...transition, + '}', + '', + '.wrap {', + ' display: inline-flex;', + '}', + '', + '.wrap svg {', + ' width: 100%;', + ' height: 100%;', + ...transition, + '}', + '', + ].join('\n') +} + +function manifestData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact) { + return { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + componentName: `${toPascalCase(config.name)}Icon`, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } +} + +function generateManifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const serialized = JSON.stringify(manifestData(config, artifact), null, 2) + .replace('"__SPRITE_URL__"', 'spriteUrl') + return [ + blockHeader(config.generatedNotice), + "import spriteUrl from './sprite.svg?no-inline'", + '', + `export const spriteManifest = ${serialized}`, + '', + 'export default spriteManifest', + '', + ].join('\n') +} + +function generateManifestDeclarations(config: ResolvedSpriteConfig): string { + return [ + blockHeader(config.generatedNotice), + "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') +} + +export function generateOutputFiles( + config: ResolvedSpriteConfig, + artifact: CompiledSpriteArtifact, +): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: generateIndex(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: generateIndexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: markedSvg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: generateIconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: generateIconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: generateComponent(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: generateComponentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: generateStyles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: generateManifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: generateManifestDeclarations(config) }, + ] +} diff --git a/src/modes/react-webpack/adapter.ts b/src/modes/react-webpack/adapter.ts new file mode 100644 index 0000000..cebb82e --- /dev/null +++ b/src/modes/react-webpack/adapter.ts @@ -0,0 +1,26 @@ +import { compileSpriteContent } from '../../compiler.js' +import { createCompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import type { ModeAdapter } from '../../core/mode-adapter.js' +import { generateOutputFiles } from './output.js' + +export const reactWebpackAdapter: ModeAdapter<'react@webpack'> = { + mode: 'react@webpack', + contractVersion: 5, + + async generate(context) { + const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { + rootViewBox: false, + }) + const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') + return { + files: generateOutputFiles(context.config, artifact), + paths: { + generatedDir: '.svg-sprite', + sprite: '.svg-sprite/sprite.svg', + manifest: '.svg-sprite/svg-sprite.manifest.js', + entry: '.svg-sprite/index.js', + }, + result: { target: 'webpack' }, + } + }, +} diff --git a/src/modes/react-webpack/output.ts b/src/modes/react-webpack/output.ts new file mode 100644 index 0000000..425323e --- /dev/null +++ b/src/modes/react-webpack/output.ts @@ -0,0 +1,187 @@ +import path from 'node:path' +import type { CompiledSpriteArtifact } from '../../core/compiled-artifact.js' +import { GENERATED_MARKER } from '../../core/generated-markers.js' +import type { GeneratedFile } from '../../core/mode-adapter.js' +import type { ResolvedSpriteConfig } from '../../types.js' + +const MODE = 'react@webpack' +const TARGET = 'webpack' +const OUTPUT_DIR = '.svg-sprite' +const NOTICE = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную.' + +function pascal(value: string): string { + return value.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('') +} + +function camel(value: string): string { + return value.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) +} + +function header(enabled: boolean): string { + return enabled + ? `/*\n * ${NOTICE}\n * ${GENERATED_MARKER}.\n */` + : `/* ${GENERATED_MARKER}. Do not edit. */` +} + +function svg(bytes: Uint8Array, enabled: boolean): string { + const marker = enabled ? `${NOTICE}\n ${GENERATED_MARKER}.` : `${GENERATED_MARKER}. Do not edit.` + const content = new TextDecoder().decode(bytes) + return content.startsWith(')\s*/, `$1\n\n`) + : `\n${content}` +} + +function comment(config: ResolvedSpriteConfig): string[] { + const text = config.description ?? `Имена иконок SVG-спрайта «${config.name}».` + return ['/**', ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), ' */'] +} + +function iconData(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const prefix = camel(config.name) + return [ + header(config.generatedNotice), + '', + ...comment(config), + `export const ${prefix}IconNames = ${JSON.stringify(artifact.icons.map((icon) => icon.name), null, 2)}`, + '', + `export const ${prefix}IconIds = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.id])), null, 2)}`, + '', + ].join('\n') +} + +function component(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + "import { jsx } from 'react/jsx-runtime'", + "import styles from './react-component.module.css'", + `import { ${ids} } from '../icon-data.js'`, + '', + "const spriteUrl = new URL('../sprite.svg', import.meta.url).href", + '', + `export const ${componentName} = (props) => {`, + ' const { icon, wrapped, className, ...rest } = props', + ` const href = spriteUrl + '#' + ${ids}[icon]`, + ' if (wrapped) {', + " return jsx('span', {", + ' ...rest,', + " className: [styles.wrap, className].filter(Boolean).join(' '),", + " children: jsx('svg', { children: jsx('use', { href }) }),", + ' })', + ' }', + " return jsx('svg', {", + ' ...rest,', + " className: [styles.root, className].filter(Boolean).join(' '),", + " children: jsx('use', { href }),", + ' })', + '}', + '', + ].join('\n') +} + +function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const names = `${camel(config.name)}IconNames` + const ids = `${camel(config.name)}IconIds` + return [ + header(config.generatedNotice), + '', + ...comment(config), + `export declare const ${names}: readonly [`, + ...artifact.icons.map((icon) => ` ${JSON.stringify(icon.name)},`), + ']', + `export type ${typeName} = typeof ${names}[number]`, + `export declare const ${ids}: {`, + ...artifact.icons.map((icon) => ` readonly ${JSON.stringify(icon.name)}: ${JSON.stringify(icon.id)}`), + '}', + '', + ].join('\n') +} + +function componentDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const typeName = `${componentName}Name` + const styleName = `${componentName}Style` + const propsName = `${componentName}Props` + return [ + header(config.generatedNotice), + "import type { CSSProperties, HTMLAttributes, ReactElement, SVGAttributes } from 'react'", + `import type { ${typeName} } from '../icon-data.js'`, + '', + `export type ${styleName} = CSSProperties & Partial>`, + `type ${componentName}BaseProps = { icon: ${typeName} }`, + `type ${componentName}SvgProps = ${componentName}BaseProps & { wrapped?: false } & Omit, 'style'> & { style?: ${styleName} }`, + `type ${componentName}WrappedProps = ${componentName}BaseProps & { wrapped: true } & Omit, 'style'> & { style?: ${styleName} }`, + `export type ${propsName} = ${componentName}SvgProps | ${componentName}WrappedProps`, + `export declare const ${componentName}: (props: ${propsName}) => ReactElement`, + '', + ].join('\n') +} + +function indexDeclarations(config: ResolvedSpriteConfig): string { + const componentName = `${pascal(config.name)}Icon` + const names = `${camel(config.name)}IconNames` + return [ + header(config.generatedNotice), + `export { ${componentName} } from './react/react-component.js'`, + `export type { ${componentName}Props, ${componentName}Style } from './react/react-component.js'`, + `export { ${names} } from './icon-data.js'`, + `export type { ${componentName}Name } from './icon-data.js'`, + '', + ].join('\n') +} + +function index(config: ResolvedSpriteConfig): string { + return [ + header(config.generatedNotice), + `export { ${pascal(config.name)}Icon } from './react/react-component.js'`, + `export { ${camel(config.name)}IconNames } from './icon-data.js'`, + '', + ].join('\n') +} + +function styles(config: ResolvedSpriteConfig): string { + const transition = config.transform.addTransition + ? [' transition-property: fill, stroke, color;', ' transition-duration: 0.3s;', ' transition-timing-function: ease;'] + : [] + return [header(config.generatedNotice), '', '.root {', ...transition, '}', '', '.wrap {', ' display: inline-flex;', '}', '', '.wrap svg {', ' width: 100%;', ' height: 100%;', ...transition, '}', ''].join('\n') +} + +function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { + const data = { + schemaVersion: 1, + generator: '@gromlab/svg-sprites', + name: config.name, + ...(config.description === undefined ? {} : { description: config.description }), + componentName: `${pascal(config.name)}Icon`, + mode: MODE, + target: TARGET, + format: artifact.format, + iconCount: artifact.icons.length, + spriteUrl: '__SPRITE_URL__', + icons: artifact.icons, + } + const source = JSON.stringify(data, null, 2).replace('"__SPRITE_URL__"', 'spriteUrl') + return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') +} + +function manifestTypes(config: ResolvedSpriteConfig): string { + return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') +} + +export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { + return [ + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'icon-data.d.ts'), content: iconDeclarations(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.js'), content: component(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.d.ts'), content: componentDeclarations(config) }, + { path: path.posix.join(OUTPUT_DIR, 'react', 'react-component.module.css'), content: styles(config) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.js'), content: manifest(config, artifact) }, + { path: path.posix.join(OUTPUT_DIR, 'svg-sprite.manifest.d.ts'), content: manifestTypes(config) }, + ] +} diff --git a/src/modes/react/codegen.ts b/src/modes/react/codegen.ts deleted file mode 100644 index bcd95b2..0000000 --- a/src/modes/react/codegen.ts +++ /dev/null @@ -1,288 +0,0 @@ -import path from 'node:path' -import { getSpriteShapeId } from '../../shape-id.js' -import { generateReactAssetUrlCode } from '../../targets/index.js' -import type { SpriteAssetTarget } from '../../targets/types.js' -import type { SpriteFormat } from '../../types.js' -import { generateSpriteManifest } from './manifest.js' -import { toPascalCase } from './naming.js' -import type { ResolvedReactSpriteConfig } from './types.js' - -const GENERATED_MARKER = '@generated by @gromlab/svg-sprites. Do not edit.' -const GENERATED_NOTICE = [ - '----------------------------------------------------------------------', - '## АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ ##', - '## ##', - '## Не редактируйте вручную: изменения будут перезаписаны. ##', - '## Для изменений перегенерируйте SVG-спрайт. ##', - '## ##', - '## Генератор: @gromlab/svg-sprites ##', - '## Репозиторий: https://github.com/gromlab-ru/svg-sprites ##', - '----------------------------------------------------------------------', -] - -type ReactCodegenOptions = { - config: ResolvedReactSpriteConfig - format: SpriteFormat - iconNames: string[] - sprite: Uint8Array - target: SpriteAssetTarget -} - -export type GeneratedFile = { - path: string - content: string | Uint8Array -} - -function toCamelCase(name: string): string { - return name.replace(/-([a-z0-9])/g, (_, char: string) => char.toUpperCase()) -} - -function generateBlockHeader(generatedNotice: boolean): string { - if (!generatedNotice) return `/* ${GENERATED_MARKER} */` - - return [ - '/*', - ...GENERATED_NOTICE.map((line) => ` * ${line}`), - ' */', - ].join('\n') -} - -function generateSvgHeader(generatedNotice: boolean): string { - if (!generatedNotice) return `` - - return [ - '', - ].join('\n') -} - -function generateGitignore(): string { - return `# ${GENERATED_MARKER}\n/generated/\n/index.ts\n/manifest.ts\n` -} - -function formatSvg(content: string): string { - let depth = 0 - const lines = content.replace(/>\n<').split('\n') - - const formatted = lines.map((line) => { - const trimmed = line.trim() - const tags = trimmed.match(/<[^>]+>/g) ?? [] - const startsWithClosingTag = trimmed.startsWith('')) { - depth += 1 - } - } - - return formattedLine - }) - - return `${formatted.join('\n')}\n` -} - -function markSvg(content: Uint8Array, generatedNotice: boolean): string { - const svg = formatSvg(new TextDecoder().decode(content)) - const header = generateSvgHeader(generatedNotice) - return svg.startsWith(')\s*/, `$1\n${header}\n`) - : `${header}\n${svg}` -} - -function generateDescriptionComment(name: string, description?: string): string[] { - const text = description ?? `Имена иконок SVG-спрайта «${name}».` - return [ - '/**', - ...text.split(/\r?\n/).map((line) => ` * ${line.replace(/\*\//g, '* /')}`), - ' */', - ] -} - -function generateTypes( - name: string, - description: string | undefined, - iconNames: string[], - generatedNotice: boolean, -): string { - const pascalName = toPascalCase(name) - const camelName = toCamelCase(name) - const iconNamesVariable = `${camelName}IconNames` - - return [ - generateBlockHeader(generatedNotice), - '', - ...generateDescriptionComment(name, description), - `export const ${iconNamesVariable} = [`, - ...iconNames.map((iconName) => ` ${JSON.stringify(iconName)},`), - '] as const', - '', - `export type ${pascalName}IconName = typeof ${iconNamesVariable}[number]`, - '', - ].join('\n') -} - -function generateComponent( - name: string, - generatedNotice: boolean, - target: SpriteAssetTarget, - iconNames: string[], -): string { - const pascalName = toPascalCase(name) - const assetUrlCode = generateReactAssetUrlCode(target, 'sprite.svg') - - return [ - generateBlockHeader(generatedNotice), - "import type { CSSProperties, HTMLAttributes, SVGAttributes } from 'react'", - "import styles from './styles.module.css'", - `import type { ${pascalName}IconName } from './types'`, - ...assetUrlCode.imports, - '', - ...assetUrlCode.declarations, - ...(assetUrlCode.declarations.length > 0 ? [''] : []), - `const iconIds: Record<${pascalName}IconName, string> = {`, - ...iconNames.map((iconName) => ` ${JSON.stringify(iconName)}: ${JSON.stringify(getSpriteShapeId(iconName))},`), - '}', - '', - `export type ${pascalName}IconStyle = CSSProperties & Partial>`, - '', - `type ${pascalName}IconBaseProps = {`, - ` icon: ${pascalName}IconName`, - '}', - '', - `type ${pascalName}IconSvgProps = ${pascalName}IconBaseProps & {`, - ' wrapped?: false', - `} & Omit, 'style'> & {`, - ` style?: ${pascalName}IconStyle`, - '}', - '', - `type ${pascalName}IconWrappedProps = ${pascalName}IconBaseProps & {`, - ' wrapped: true', - `} & Omit, 'style'> & {`, - ` style?: ${pascalName}IconStyle`, - '}', - '', - `export type ${pascalName}IconProps =`, - ` | ${pascalName}IconSvgProps`, - ` | ${pascalName}IconWrappedProps`, - '', - `/** Иконка из SVG-спрайта «${name}». */`, - `export const ${pascalName}Icon = (props: ${pascalName}IconProps) => {`, - ' const { icon, wrapped, className, ...rest } = props', - ` const href = ${assetUrlCode.variableName} + '#' + iconIds[icon]`, - '', - ' if (wrapped) {', - ' const htmlAttributes = rest as HTMLAttributes', - ' return (', - " ", - ' ', - ' ', - ' ', - ' ', - ' )', - ' }', - '', - ' const svgAttributes = rest as SVGAttributes', - ' return (', - " ", - ' ', - ' ', - ' )', - '}', - '', - ].join('\n') -} - -function generateCss(generatedNotice: boolean, addTransition: boolean): string { - const transitionRules = addTransition - ? [ - ' transition-property: fill, stroke, color;', - ' transition-duration: 0.3s;', - ' transition-timing-function: ease;', - ] - : [] - - return [ - generateBlockHeader(generatedNotice), - '', - '.root {', - ...transitionRules, - '}', - '', - '.wrap {', - ' display: inline-flex;', - '}', - '', - '.wrap svg {', - ' width: 100%;', - ' height: 100%;', - ...transitionRules, - '}', - '', - ].join('\n') -} - -function generateRootIndex(name: string, generatedNotice: boolean): string { - const pascalName = toPascalCase(name) - const iconNamesVariable = `${toCamelCase(name)}IconNames` - - return [ - generateBlockHeader(generatedNotice), - `export { ${pascalName}Icon } from './generated/react-component'`, - `export type { ${pascalName}IconProps, ${pascalName}IconStyle } from './generated/react-component'`, - `export { ${iconNamesVariable} } from './generated/types'`, - `export type { ${pascalName}IconName } from './generated/types'`, - '', - ].join('\n') -} - -/** Создаёт все управляемые файлы React sprite-модуля в памяти. */ -export function generateReactFiles(options: ReactCodegenOptions): GeneratedFile[] { - const { config, format, iconNames, sprite, target } = options - const { name, description, generatedNotice } = config - - return [ - { - path: '.gitignore', - content: generateGitignore(), - }, - { - path: path.posix.join('generated', 'sprite.svg'), - content: markSvg(sprite, generatedNotice), - }, - { - path: path.posix.join('generated', 'types.ts'), - content: generateTypes(name, description, iconNames, generatedNotice), - }, - { - path: path.posix.join('generated', 'react-component.tsx'), - content: generateComponent(name, generatedNotice, target, iconNames), - }, - { - path: path.posix.join('generated', 'styles.module.css'), - content: generateCss(generatedNotice, config.transform.addTransition ?? true), - }, - { - path: 'manifest.ts', - content: generateSpriteManifest({ - header: generateBlockHeader(generatedNotice), - name, - description, - format, - iconNames, - sprite, - target, - }), - }, - { - path: 'index.ts', - content: generateRootIndex(name, generatedNotice), - }, - ] -} diff --git a/src/modes/react/config.ts b/src/modes/react/config.ts deleted file mode 100644 index 2329b3c..0000000 --- a/src/modes/react/config.ts +++ /dev/null @@ -1,108 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { createJiti } from 'jiti' -import { toKebabCase, validateSpriteName } from './naming.js' -import type { ReactSpriteConfig, ResolvedReactSpriteConfig } from './types.js' - -export const REACT_CONFIG_FILE = 'svg-sprite.config.ts' - -function getDefaultName(rootDir: string): string { - const rootName = path.basename(rootDir) - const source = rootName === 'svg-sprite' || rootName === 'svg-sprites' - ? path.basename(path.dirname(rootDir)) - : rootName - const name = toKebabCase(source) - - if (!name) { - throw new Error(`Cannot infer sprite name from directory: ${rootDir}`) - } - - return name -} - -/** Загружает локальный React-конфиг из корня sprite-модуля. */ -export async function loadReactSpriteConfig(rootDir: string): Promise { - const configPath = path.join(rootDir, REACT_CONFIG_FILE) - - if (!fs.existsSync(configPath)) { - throw new Error( - `React config file not found: ${configPath}\n` + - `Create ${REACT_CONFIG_FILE} inside the sprite directory.`, - ) - } - - const jiti = createJiti(rootDir) - const mod = await jiti.import(configPath) as { default?: ReactSpriteConfig } - const config = mod.default - - if (!config || typeof config !== 'object') { - throw new Error( - `React config file must have a default export: ${configPath}\n` + - 'Use: export default defineReactSpriteConfig({ ... })', - ) - } - - if (config.name !== undefined && typeof config.name !== 'string') { - throw new Error('React config: "name" must be a string.') - } - - if (config.description !== undefined && typeof config.description !== 'string') { - throw new Error('React config: "description" must be a string.') - } - - if ('icons' in config) { - throw new Error('React config: "icons" was renamed to "inputFolder".') - } - - if (config.inputFolder !== undefined && ( - typeof config.inputFolder !== 'string' || config.inputFolder.trim() === '' - )) { - throw new Error('React config: "inputFolder" must be a non-empty string.') - } - - if (config.inputFiles !== undefined && ( - !Array.isArray(config.inputFiles) - || config.inputFiles.some((filePath) => typeof filePath !== 'string' || filePath.trim() === '') - )) { - throw new Error('React config: "inputFiles" must be an array of non-empty strings.') - } - - if (config.transform !== undefined) { - if ( - config.transform === null - || typeof config.transform !== 'object' - || Array.isArray(config.transform) - ) { - throw new Error('React config: "transform" must be an object.') - } - - for (const option of ['removeSize', 'replaceColors', 'addTransition'] as const) { - if (config.transform[option] !== undefined && typeof config.transform[option] !== 'boolean') { - throw new Error(`React config: "transform.${option}" must be a boolean.`) - } - } - } - - if (config.generatedNotice !== undefined && typeof config.generatedNotice !== 'boolean') { - throw new Error('React config: "generatedNotice" must be a boolean.') - } - - const name = config.name ?? getDefaultName(rootDir) - validateSpriteName(name) - const inputFiles = (config.inputFiles ?? []).map((filePath) => path.resolve(rootDir, filePath)) - const defaultInputFolder = path.resolve(rootDir, 'icons') - const inputFolder = config.inputFolder === undefined - && inputFiles.length > 0 - && !fs.existsSync(defaultInputFolder) - ? null - : path.resolve(rootDir, config.inputFolder ?? 'icons') - - return { - name, - description: config.description, - inputFolder, - inputFiles, - transform: { ...config.transform }, - generatedNotice: config.generatedNotice ?? true, - } -} diff --git a/src/modes/react/generate.ts b/src/modes/react/generate.ts deleted file mode 100644 index b7aef82..0000000 --- a/src/modes/react/generate.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ReactAssetTarget } from '../../targets/types.js' -import { generateSpriteModule } from './module-generator.js' -import type { ReactSpriteGenerationResult } from './types.js' - -/** Генерирует один локальный React sprite-модуль для Vite или Webpack. */ -export function generateReactSprite( - root: string, - target: ReactAssetTarget, -): Promise { - if (target !== 'vite' && target !== 'webpack') { - throw new Error(`Unsupported React asset target: ${String(target)}`) - } - - return generateSpriteModule(root, target, { - mode: `react@${target}`, - rootViewBox: false, - }) -} diff --git a/src/modes/react/index.ts b/src/modes/react/index.ts deleted file mode 100644 index c4e7530..0000000 --- a/src/modes/react/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { generateReactSprite } from './generate.js' -export { loadReactSpriteConfig, REACT_CONFIG_FILE } from './config.js' -export type { - ReactSpriteConfig, - ReactSpriteGenerationResult, - ResolvedReactSpriteConfig, -} from './types.js' diff --git a/src/modes/react/manifest.ts b/src/modes/react/manifest.ts deleted file mode 100644 index 0d84eb3..0000000 --- a/src/modes/react/manifest.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { getSpriteShapeId } from '../../shape-id.js' -import { generateReactAssetUrlCode } from '../../targets/index.js' -import type { SpriteAssetTarget } from '../../targets/types.js' -import type { SpriteFormat } from '../../types.js' -import { toPascalCase } from './naming.js' - -type ManifestIconColor = { - variable: string - fallback: string -} - -type ManifestIcon = { - name: string - id: string - viewBox: string | null - colors: ManifestIconColor[] -} - -type GenerateManifestOptions = { - header: string - name: string - description?: string - format: SpriteFormat - iconNames: string[] - sprite: Uint8Array - target: SpriteAssetTarget -} - -function extractColors(fragment: string): ManifestIconColor[] { - const colors = new Map() - const regex = /var\((--icon-color-\d+),\s*((?:[^()]|\([^()]*\))*)\)/g - let match: RegExpExecArray | null - - while ((match = regex.exec(fragment)) !== null) { - if (!colors.has(match[1])) colors.set(match[1], match[2].trim()) - } - - return [...colors].map(([variable, fallback]) => ({ variable, fallback })) -} - -function extractManifestIcons(sprite: Uint8Array, iconNames: string[]): ManifestIcon[] { - const shapes = new Map() - const content = new TextDecoder().decode(sprite) - const shapeRegex = /<(symbol|svg)\b((?=[^>]*\bid="[^"]+")[^>]*)>[\s\S]*?<\/\1>/g - let match: RegExpExecArray | null - - while ((match = shapeRegex.exec(content)) !== null) { - const attributes = match[2] - const id = attributes.match(/\bid="([^"]+)"/)?.[1] - if (!id) continue - - shapes.set(id, { - viewBox: attributes.match(/\bviewBox="([^"]+)"/)?.[1] ?? null, - fragment: match[0], - }) - } - - return iconNames.map((name) => { - const id = getSpriteShapeId(name) - const shape = shapes.get(id) - if (!shape) throw new Error(`Cannot find SVG shape "${id}" for icon "${name}".`) - - return { - name, - id, - viewBox: shape.viewBox, - colors: extractColors(shape.fragment), - } - }) -} - -function generateIcon(icon: ManifestIcon): string[] { - return [ - ' {', - ` name: ${JSON.stringify(icon.name)},`, - ` id: ${JSON.stringify(icon.id)},`, - ` viewBox: ${JSON.stringify(icon.viewBox)},`, - ' colors: [', - ...icon.colors.flatMap((color) => [ - ' {', - ` variable: ${JSON.stringify(color.variable)},`, - ` fallback: ${JSON.stringify(color.fallback)},`, - ' },', - ]), - ' ],', - ' },', - ] -} - -/** Генерирует отдельную debug-точку входа с метаданными спрайта. */ -export function generateSpriteManifest(options: GenerateManifestOptions): string { - const { header, name, description, format, iconNames, sprite, target } = options - const assetUrlCode = generateReactAssetUrlCode(target, 'generated/sprite.svg') - const icons = extractManifestIcons(sprite, iconNames) - - return [ - header, - ...assetUrlCode.imports, - ...(assetUrlCode.imports.length > 0 ? [''] : []), - ...assetUrlCode.declarations, - ...(assetUrlCode.declarations.length > 0 ? [''] : []), - 'export const spriteManifest = {', - ' schemaVersion: 1,', - " generator: '@gromlab/svg-sprites',", - ` name: ${JSON.stringify(name)},`, - ...(description === undefined ? [] : [` description: ${JSON.stringify(description)},`]), - ` componentName: ${JSON.stringify(`${toPascalCase(name)}Icon`)},`, - ` target: ${JSON.stringify(target)},`, - ` format: ${JSON.stringify(format)},`, - ` iconCount: ${icons.length},`, - ` spriteUrl: ${assetUrlCode.variableName},`, - ' icons: [', - ...icons.flatMap(generateIcon), - ' ],', - '} as const', - '', - 'export default spriteManifest', - '', - ].join('\n') -} diff --git a/src/modes/react/module-generator.ts b/src/modes/react/module-generator.ts deleted file mode 100644 index af0caf4..0000000 --- a/src/modes/react/module-generator.ts +++ /dev/null @@ -1,78 +0,0 @@ -import path from 'node:path' -import { compileSpriteContent } from '../../compiler.js' -import { log } from '../../logger.js' -import { resolveSpriteSources } from '../../scanner.js' -import { getSpriteShapeId } from '../../shape-id.js' -import type { SpriteAssetTarget } from '../../targets/types.js' -import { generateReactFiles } from './codegen.js' -import { loadReactSpriteConfig } from './config.js' -import type { SpriteModuleGenerationResult } from './types.js' -import { writeReactFiles } from './writer.js' - -type GenerateSpriteModuleOptions = { - mode: string - rootViewBox: boolean -} - -function validateIconIds(iconNames: string[]): void { - const namesById = new Map() - - for (const iconName of iconNames) { - const id = getSpriteShapeId(iconName) - const existingName = namesById.get(id) - - if (existingName) { - throw new Error( - `Icons "${existingName}" and "${iconName}" produce the same SVG id "${id}". Rename one of the files.`, - ) - } - - namesById.set(id, iconName) - } -} - -/** Общая генерация типизированного React-компонента для React и Next.js modes. */ -export async function generateSpriteModule( - root: string, - target: TTarget, - options: GenerateSpriteModuleOptions, -): Promise> { - const rootDir = path.resolve(root) - const config = await loadReactSpriteConfig(rootDir) - const format = 'stack' - const folder = resolveSpriteSources({ - name: config.name, - format, - inputFolder: config.inputFolder, - inputFiles: config.inputFiles, - }) - - const iconNames = folder.files - .map((filePath) => path.basename(filePath, '.svg')) - .sort() - validateIconIds(iconNames) - - const sprite = await compileSpriteContent(folder, config.transform, { - rootViewBox: options.rootViewBox, - }) - const files = generateReactFiles({ config, format, iconNames, sprite, target }) - - writeReactFiles(rootDir, files, config.generatedNotice) - - const generatedDir = path.join(rootDir, 'generated') - const spritePath = path.join(generatedDir, 'sprite.svg') - const manifestPath = path.join(rootDir, 'manifest.ts') - const iconLabel = iconNames.length === 1 ? 'icon' : 'icons' - log.success(`✓ ${config.name} · ${iconNames.length} ${iconLabel} · ${options.mode}`) - log.detail(` → ${path.relative(process.cwd(), generatedDir)}`) - - return { - name: config.name, - rootDir, - generatedDir, - spritePath, - manifestPath, - iconCount: iconNames.length, - target, - } -} diff --git a/src/modes/react/naming.ts b/src/modes/react/naming.ts deleted file mode 100644 index 3de2340..0000000 --- a/src/modes/react/naming.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** Преобразует kebab-case имя в PascalCase. */ -export function toPascalCase(value: string): string { - return value - .split('-') - .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) - .join('') -} - -/** Преобразует имя папки в допустимое kebab-case имя спрайта. */ -export function toKebabCase(value: string): string { - return value - .normalize('NFKD') - .replace(/([a-z0-9])([A-Z])/g, '$1-$2') - .replace(/[^a-zA-Z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') - .toLowerCase() -} - -export function validateSpriteName(name: string): void { - if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(name)) { - throw new Error( - `React config: "name" must be kebab-case and start with a letter. Received: "${name}".`, - ) - } -} diff --git a/src/modes/react/types.ts b/src/modes/react/types.ts deleted file mode 100644 index 21c5f64..0000000 --- a/src/modes/react/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { - ReactAssetTarget, - SpriteAssetTarget, -} from '../../targets/types.js' -import type { TransformOptions } from '../../types.js' - -export type ReactSpriteConfig = { - /** Логическое имя спрайта. По умолчанию выводится из пути модуля. */ - name?: string - /** Описание спрайта для документации и будущего инспектора. */ - description?: string - /** Папка с исходными SVG относительно svg-sprite.config.ts. По умолчанию: ./icons. */ - inputFolder?: string - /** Дополнительные SVG-файлы относительно svg-sprite.config.ts. По умолчанию: []. */ - inputFiles?: string[] - /** Настройки трансформации SVG. По умолчанию все трансформации включены. */ - transform?: TransformOptions - /** Добавлять развёрнутое предупреждение в generated-файлы. По умолчанию: true. */ - generatedNotice?: boolean -} - -export type ResolvedReactSpriteConfig = { - name: string - description?: string - inputFolder: string | null - inputFiles: string[] - transform: TransformOptions - generatedNotice: boolean -} - -export type SpriteModuleGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - /** Среда, для которой сгенерирован способ получения URL SVG asset. */ - target: TTarget -} - -export type ReactSpriteGenerationResult = SpriteModuleGenerationResult diff --git a/src/modes/react/writer.ts b/src/modes/react/writer.ts deleted file mode 100644 index 3f028f1..0000000 --- a/src/modes/react/writer.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { randomUUID } from 'node:crypto' -import fs from 'node:fs' -import path from 'node:path' -import type { GeneratedFile } from './codegen.js' - -const MANIFEST_FILE = '.svg-sprites.manifest.json' -const GENERATOR = '@gromlab/svg-sprites' -const GENERATED_MARKER = '@generated by @gromlab/svg-sprites' -const GENERATED_NOTICE_MARKER = 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ' -const ROOT_MANAGED_FILES = new Set(['.gitignore', 'index.ts', 'manifest.ts']) - -type Manifest = { - version: 1 - generator: typeof GENERATOR - files: string[] - warning?: string -} - -function normalizeManagedPath(relativePath: string): string { - const normalized = relativePath.replaceAll('\\', '/') - const parts = normalized.split('/') - const isRootFile = ROOT_MANAGED_FILES.has(normalized) - const isGeneratedFile = parts.length === 2 - && parts[0] === 'generated' - && parts[1] !== '' - && parts[1] !== '.' - && parts[1] !== '..' - - if (!isRootFile && !isGeneratedFile) { - throw new Error(`Invalid generated file path: ${relativePath}`) - } - - return normalized -} - -function resolveManagedPath(rootDir: string, relativePath: string): string { - const normalized = normalizeManagedPath(relativePath) - const resolved = path.resolve(rootDir, normalized) - const relative = path.relative(rootDir, resolved) - - if (relative.startsWith('..') || path.isAbsolute(relative)) { - throw new Error(`Invalid generated file path: ${relativePath}`) - } - - return resolved -} - -function assertNoSymlinks(rootDir: string, filePath: string): void { - const relative = path.relative(rootDir, filePath) - let currentPath = rootDir - - for (const segment of relative.split(path.sep)) { - currentPath = path.join(currentPath, segment) - try { - if (fs.lstatSync(currentPath).isSymbolicLink()) { - throw new Error(`Symbolic links are not allowed in generated paths: ${currentPath}`) - } - } catch (error) { - if (error instanceof Error && 'code' in error && error.code === 'ENOENT') continue - throw error - } - } -} - -function readManifest(rootDir: string, manifestPath: string): Manifest | null { - assertNoSymlinks(rootDir, manifestPath) - if (!fs.existsSync(manifestPath)) return null - - let manifest: unknown - try { - manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) - } catch { - throw new Error(`Cannot parse generated files manifest: ${manifestPath}`) - } - - if ( - !manifest - || typeof manifest !== 'object' - || !('version' in manifest) - || manifest.version !== 1 - || !('generator' in manifest) - || manifest.generator !== GENERATOR - || !('files' in manifest) - || !Array.isArray(manifest.files) - || !manifest.files.every((file) => typeof file === 'string') - ) { - throw new Error(`Invalid generated files manifest: ${manifestPath}`) - } - - return { - ...(manifest as Manifest), - files: (manifest as Manifest).files.map(normalizeManagedPath), - } -} - -function hasGeneratedMarker(filePath: string): boolean { - if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return false - const content = fs.readFileSync(filePath, 'utf-8') - return content.includes(GENERATED_MARKER) || content.includes(GENERATED_NOTICE_MARKER) -} - -function writeFileAtomic(rootDir: string, filePath: string, content: string | Uint8Array): void { - assertNoSymlinks(rootDir, filePath) - fs.mkdirSync(path.dirname(filePath), { recursive: true }) - assertNoSymlinks(rootDir, filePath) - - const temporaryPath = path.join( - path.dirname(filePath), - `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`, - ) - - try { - fs.writeFileSync(temporaryPath, content, { flag: 'wx' }) - fs.renameSync(temporaryPath, filePath) - } finally { - if (fs.existsSync(temporaryPath)) fs.unlinkSync(temporaryPath) - } -} - -/** Обновляет только файлы, которыми владеет React-генератор. */ -export function writeReactFiles( - rootDir: string, - files: GeneratedFile[], - generatedNotice: boolean, -): void { - const generatedDir = path.join(rootDir, 'generated') - const manifestPath = path.join(generatedDir, MANIFEST_FILE) - const previousManifest = readManifest(rootDir, manifestPath) - const previousFiles = new Set(previousManifest?.files ?? []) - const nextFiles = files.map((file) => normalizeManagedPath(file.path)) - const obsoleteFiles: string[] = [] - - for (const file of files) { - const filePath = resolveManagedPath(rootDir, file.path) - assertNoSymlinks(rootDir, filePath) - if (fs.existsSync(filePath) && !hasGeneratedMarker(filePath)) { - throw new Error( - `Refusing to overwrite a user file: ${filePath}\n` + - 'Move the file or choose another sprite directory.', - ) - } - } - - for (const relativePath of previousFiles) { - if (nextFiles.includes(relativePath)) continue - const filePath = resolveManagedPath(rootDir, relativePath) - assertNoSymlinks(rootDir, filePath) - - if (fs.existsSync(filePath)) { - if (!hasGeneratedMarker(filePath)) { - throw new Error(`Refusing to delete a user file: ${filePath}`) - } - obsoleteFiles.push(filePath) - } - } - - for (const filePath of obsoleteFiles) fs.unlinkSync(filePath) - - for (const file of files) { - writeFileAtomic(rootDir, resolveManagedPath(rootDir, file.path), file.content) - } - - const manifest: Manifest = { - version: 1, - generator: GENERATOR, - files: nextFiles, - ...(generatedNotice && { - warning: 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную: изменения будут перезаписаны.', - }), - } - writeFileAtomic(rootDir, manifestPath, `${JSON.stringify(manifest, null, 2)}\n`) -} diff --git a/src/preview.ts b/src/preview.ts deleted file mode 100644 index 1eeeaa8..0000000 --- a/src/preview.ts +++ /dev/null @@ -1,185 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import type { SpriteResult } from './types.js' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) - -/** Извлекает id иконок из SVG-спрайта. */ -function extractIconIds(spritePath: string): string[] { - const content = fs.readFileSync(spritePath, 'utf-8') - const ids: string[] = [] - const regex = /<(?:svg|symbol)\b[^>]*\bid="([^"]+)"/g - let match: RegExpExecArray | null - while ((match = regex.exec(content)) !== null) { - ids.push(match[1]) - } - return ids.sort() -} - -/** Извлекает CSS-переменные var(--icon-color-N, fallback) из SVG-фрагмента иконки. */ -function extractIconVars(svgFragment: string): { varName: string; fallback: string }[] { - const vars = new Map() - const regex = /var\((--icon-color-\d+),\s*([^)]+)\)/g - let match: RegExpExecArray | null - while ((match = regex.exec(svgFragment)) !== null) { - if (!vars.has(match[1])) { - vars.set(match[1], match[2].trim()) - } - } - return [...vars.entries()].map(([varName, fallback]) => ({ varName, fallback })) -} - -/** Парсит SVG-спрайт и возвращает маппинг id → SVG-фрагмент. */ -function extractIconFragments(spritePath: string): Map { - const content = fs.readFileSync(spritePath, 'utf-8') - const fragments = new Map() - const regex = /<(?:svg|symbol)\b[^>]*\bid="([^"]+)"[^>]*>[\s\S]*?<\/(?:svg|symbol)>/g - let match: RegExpExecArray | null - while ((match = regex.exec(content)) !== null) { - fragments.set(match[1], match[0]) - } - return fragments -} - -/** Извлекает viewBox из SVG-фрагмента иконки. */ -function extractViewBox(svgFragment: string): { x: number; y: number; width: number; height: number } | null { - const match = svgFragment.match(/viewBox="([^"]+)"/) - if (!match) return null - const parts = match[1].split(/\s+/).map(Number) - if (parts.length !== 4) return null - return { x: parts[0], y: parts[1], width: parts[2], height: parts[3] } -} - -/** Конвертирует CSS-цвет в hex. */ -function colorToHex(color: string): string { - const named: Record = { - red: '#ff0000', blue: '#0000ff', green: '#008000', white: '#ffffff', - black: '#000000', yellow: '#ffff00', cyan: '#00ffff', magenta: '#ff00ff', - orange: '#ffa500', purple: '#800080', pink: '#ffc0cb', gray: '#808080', - grey: '#808080', currentcolor: '#000000', - } - const lower = color.toLowerCase().trim() - if (lower.startsWith('#')) { - if (lower.length === 4) return `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}` - return lower - } - return named[lower] || '#000000' -} - -/** Подготавливает SVG-спрайт для инлайна — конвертирует вложенные в . */ -function prepareInlineSprite(spritePath: string): string { - let content = fs.readFileSync(spritePath, 'utf-8') - content = content.replace(/<\?xml[^?]*\?>\s*/g, '') - content = content.replace(/' : '' - } - depth++ - if (depth > 1) { - const cleanAttrs = attrs.replace(/\s*xmlns="[^"]*"/g, '') - return `` - } - return `` - }) - return content -} - -interface IconData { - id: string - group: string - mode: string - spriteFile: string - viewBox: { x: number; y: number; width: number; height: number } | null - vars: { varName: string; fallback: string; hex: string; isCurrentColor: boolean }[] -} - -interface SpriteGroup { - name: string - mode: string - spriteFile: string - icons: IconData[] -} - -/** - * Генерирует HTML-файл превью для всех спрайтов. - * - * Использует pre-built React-приложение из dist/preview-template.html, - * инжектирует данные спрайтов и inline SVG. - */ -export function generatePreview( - results: SpriteResult[], - outputDir: string, -): string { - // Собираем данные - const groups: SpriteGroup[] = results.map((r) => { - const fragments = extractIconFragments(r.spritePath) - const ids = extractIconIds(r.spritePath) - const spriteFile = `${r.name}.sprite.svg` - - const icons: IconData[] = ids.map((id) => { - const fragment = fragments.get(id) || '' - return { - id, - group: r.name, - mode: r.format, - spriteFile, - viewBox: extractViewBox(fragment), - vars: extractIconVars(fragment).map((v) => ({ - varName: v.varName, - fallback: v.fallback, - hex: colorToHex(v.fallback), - isCurrentColor: v.fallback.toLowerCase() === 'currentcolor', - })), - } - }) - - return { name: r.name, mode: r.format, spriteFile, icons } - }) - - // Inline SVG спрайтов - const inlineSprites = results - .map((r) => prepareInlineSprite(r.spritePath)) - .join('\n') - - // Скрипт с данными + DOM injection - const svgEscaped = inlineSprites.replace(/`/g, '\\`').replace(/\$/g, '\\$') - const dataScript = [ - '', - ].join('\n') - - // Читаем шаблон - const templatePath = path.join(__dirname, 'preview-template.html') - - if (!fs.existsSync(templatePath)) { - throw new Error( - `Preview template not found: ${templatePath}\n` + - 'Run "npm run build" in the preview/ directory first.', - ) - } - - let html = fs.readFileSync(templatePath, 'utf-8') - html = html.replace('', dataScript) - - // Записываем результат - const outputPath = path.join(outputDir, 'preview.html') - fs.mkdirSync(outputDir, { recursive: true }) - fs.writeFileSync(outputPath, html) - - return outputPath -} diff --git a/src/react/types.ts b/src/react/types.ts index 23afcdb..cea60f1 100644 --- a/src/react/types.ts +++ b/src/react/types.ts @@ -1,4 +1,4 @@ -import type { SpriteAssetTarget } from '../targets/types.js' +import type { SpriteAssetTarget, SpriteMode } from '../targets/types.js' export type SpriteManifestColor = { variable: `--icon-color-${number}` @@ -19,6 +19,7 @@ export type SpriteManifest = { name: string description?: string componentName: string + mode?: SpriteMode target: SpriteAssetTarget format: 'stack' | 'symbol' iconCount: number diff --git a/src/scanner.ts b/src/scanner.ts index bd94908..055cdcf 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -1,6 +1,6 @@ import fs from 'node:fs' import path from 'node:path' -import type { SpriteEntry, SpriteFolder } from './types.js' +import type { SpriteFolder } from './types.js' /** * Сканирует папку и возвращает отсортированные абсолютные пути к SVG-файлам. @@ -61,46 +61,3 @@ export function resolveSpriteSources(options: ResolveSpriteSourcesOptions): Spri files, } } - -/** - * Преобразует SpriteEntry из конфига в SpriteFolder для компиляции. - */ -export function resolveSpriteEntry(entry: SpriteEntry): SpriteFolder { - const format = entry.format ?? 'stack' - - if (Array.isArray(entry.input)) { - const files = resolveFiles(entry.input) - - if (files.length === 0) { - throw new Error(`Sprite "${entry.name}" has empty input array.`) - } - - return { - name: entry.name, - format, - path: null, - files, - } - } - - const dirPath = path.resolve(entry.input) - const files = scanDirectory(dirPath) - - if (files.length === 0) { - throw new Error(`Sprite "${entry.name}" has no SVG files in "${dirPath}".`) - } - - return { - name: entry.name, - format, - path: dirPath, - files, - } -} - -/** - * Преобразует массив SpriteEntry из конфига в массив SpriteFolder. - */ -export function resolveSprites(entries: SpriteEntry[]): SpriteFolder[] { - return entries.map(resolveSpriteEntry) -} diff --git a/src/targets/index.ts b/src/targets/index.ts deleted file mode 100644 index bafff21..0000000 --- a/src/targets/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { generateViteAssetUrlCode } from './vite.js' -import { generateWebpackAssetUrlCode } from './webpack.js' -import type { SpriteAssetTarget, SpriteAssetUrlCode } from './types.js' - -/** Возвращает codegen-фрагменты для выбранной среды сборки React-модуля. */ -export function generateReactAssetUrlCode( - target: SpriteAssetTarget, - spriteFileName: string, -): SpriteAssetUrlCode { - switch (target) { - case 'vite': - return generateViteAssetUrlCode(spriteFileName) - case 'webpack': - case 'next@app/turbopack': - case 'next@app/webpack': - case 'next@pages/turbopack': - case 'next@pages/webpack': - return generateWebpackAssetUrlCode(spriteFileName) - default: - throw new Error(`Unsupported sprite asset target: ${String(target)}`) - } -} - -export type { - NextAssetTarget, - NextBundler, - NextRouter, - ReactAssetTarget, - SpriteAssetTarget, - SpriteAssetUrlCode, - ViteAssetTarget, - WebpackAssetTarget, -} from './types.js' diff --git a/src/targets/types.ts b/src/targets/types.ts index 77492a4..b974536 100644 --- a/src/targets/types.ts +++ b/src/targets/types.ts @@ -31,9 +31,15 @@ export type NextAssetTarget = `next@${NextRouter}/${NextBundler}` */ export type ReactAssetTarget = ViteAssetTarget | WebpackAssetTarget +/** Полный ключ React mode, используемый конфигом, CLI и manifest. */ +export type ReactSpriteMode = `react@${ReactAssetTarget}` + /** Любая среда, для которой может быть сгенерирован React sprite-модуль. */ export type SpriteAssetTarget = ReactAssetTarget | NextAssetTarget +/** Режим генерации sprite-модуля. В будущем расширяется standalone mode. */ +export type SpriteMode = ReactSpriteMode | NextAssetTarget + /** Фрагменты кода, необходимые компоненту для получения URL SVG asset. */ export type SpriteAssetUrlCode = { /** Импорты, которые добавляются в начало generated-компонента. */ diff --git a/src/targets/vite.ts b/src/targets/vite.ts deleted file mode 100644 index 1d8da1f..0000000 --- a/src/targets/vite.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { SpriteAssetUrlCode } from './types.js' - -/** Генерирует Vite-импорт отдельного, запрещённого к inline SVG asset. */ -export function generateViteAssetUrlCode(spriteFileName: string): SpriteAssetUrlCode { - return { - imports: [`import spriteUrl from './${spriteFileName}?no-inline'`], - declarations: [], - variableName: 'spriteUrl', - } -} diff --git a/src/targets/webpack.ts b/src/targets/webpack.ts deleted file mode 100644 index c06a502..0000000 --- a/src/targets/webpack.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { SpriteAssetUrlCode } from './types.js' - -/** Генерирует Webpack 5 Asset Module через статический import.meta.url. */ -export function generateWebpackAssetUrlCode(spriteFileName: string): SpriteAssetUrlCode { - return { - imports: [], - declarations: [ - `const spriteUrl = new URL('./${spriteFileName}', import.meta.url).href`, - ], - variableName: 'spriteUrl', - } -} diff --git a/src/types.ts b/src/types.ts index 335e7db..f98ab9f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,23 +1,8 @@ +import type { SpriteMode } from './targets/types.js' + /** Формат спрайта: stack или symbol. */ export type SpriteFormat = 'stack' | 'symbol' -/** Описание одного спрайта в конфиге. */ -export type SpriteEntry = { - /** Уникальное имя спрайта (используется как имя файла и в типах). */ - name: string - /** - * Источник SVG-файлов. - * Строка — путь к папке с SVG-файлами. - * Массив — пути к конкретным SVG-файлам. - */ - input: string | string[] - /** - * Формат спрайта. - * По умолчанию: 'stack'. - */ - format?: SpriteFormat -} - /** Параметры трансформации SVG. Все включены по умолчанию. */ export type TransformOptions = { /** @@ -39,22 +24,33 @@ export type TransformOptions = { addTransition?: boolean } -/** Конфигурация генерации SVG-спрайтов. */ -export type SvgSpritesConfig = { - /** Путь к папке для сгенерированных SVG-спрайтов. */ - output: string - /** - * Генерировать HTML-превью со всеми иконками. - * По умолчанию: true. - */ - preview?: boolean - /** - * Настройки трансформации SVG. - * По умолчанию: все трансформации включены. - */ +/** Единая конфигурация локального sprite-модуля. */ +export type SpriteConfig = { + /** Режим можно определить в конфиге либо передать через CLI/API. */ + mode?: SpriteMode + /** Логическое имя спрайта. По умолчанию выводится из каталога модуля. */ + name?: string + /** Описание спрайта для generated types и manifest. */ + description?: string + /** Папка с исходными SVG относительно корня модуля. По умолчанию: ./icons. */ + inputFolder?: string + /** Дополнительные SVG-файлы относительно корня модуля. По умолчанию: []. */ + inputFiles?: string[] + /** Настройки трансформации SVG. По умолчанию все включены. */ transform?: TransformOptions - /** Список спрайтов для генерации. */ - sprites: SpriteEntry[] + /** Добавлять развёрнутое предупреждение в generated-файлы. По умолчанию: true. */ + generatedNotice?: boolean +} + +/** Полностью разрешённая конфигурация, готовая к генерации. */ +export type ResolvedSpriteConfig = { + mode: SpriteMode + name: string + description?: string + inputFolder: string | null + inputFiles: string[] + transform: Required + generatedNotice: boolean } /** Результат парсинга спрайта — данные для компиляции. */ @@ -68,15 +64,3 @@ export type SpriteFolder = { /** Абсолютные пути к SVG-файлам. */ files: string[] } - -/** Результат компиляции одного спрайта. */ -export type SpriteResult = { - /** Имя спрайта. */ - name: string - /** Формат спрайта. */ - format: SpriteFormat - /** Путь к сгенерированному SVG-спрайту. */ - spritePath: string - /** Количество иконок в спрайте. */ - iconCount: number -} diff --git a/svg-sprites.config.ts b/svg-sprites.config.ts deleted file mode 100644 index a21098c..0000000 --- a/svg-sprites.config.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { defineLegacyConfig } from './src/index.js' - -export default defineLegacyConfig({ - output: 'preview/public', - preview: true, - - sprites: [ - { - name: 'icons', - input: 'test/assets/icons', - format: 'stack', - }, - { - name: 'logos', - input: 'test/assets/logos', - format: 'stack', - }, - ], -}) From 7992adc9d3daa40d9005cbf1586b85b4e334ba2a Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Mon, 13 Jul 2026 20:07:42 +0300 Subject: [PATCH 02/11] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8E=20generated-=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - описаны единая конфигурация и exact modes - обновлена структура .svg-sprite - удалены материалы legacy pipeline - синхронизированы русская и английская версии skill --- FEATURES.md | 4 +- README.md | 17 +- README_RU.md | 17 +- docs/en/legacy.md | 102 -------- docs/en/migration-1.md | 122 --------- docs/en/next-app.md | 17 +- docs/en/next-pages.md | 17 +- docs/en/programmatic-api.md | 202 +++++---------- docs/en/react-vite.md | 25 +- docs/en/react-webpack.md | 27 +- docs/en/reference.md | 112 ++++---- docs/ru/legacy.md | 102 -------- docs/ru/migration-1.md | 122 --------- docs/ru/next-app.md | 17 +- docs/ru/next-pages.md | 17 +- docs/ru/programmatic-api.md | 202 +++++---------- docs/ru/react-vite.md | 25 +- docs/ru/react-webpack.md | 27 +- docs/ru/reference.md | 112 ++++---- skills/README.md | 2 - skills/svg-sprites/skill.config.mjs | 6 +- skills/svg-sprites/src/en/SKILL.md | 1 - .../src/en/core/00-package-overview.md | 6 +- .../src/en/core/10-mode-selection.md | 18 +- .../src/en/core/20-project-inspection.md | 4 +- .../src/en/core/30-react-next-setup.md | 13 +- .../src/en/core/40-generated-contract.md | 32 ++- .../src/en/core/50-usage-and-colors.md | 2 +- .../src/en/core/60-verification.md | 6 +- .../svg-sprites/src/en/core/70-diagnostics.md | 13 +- .../src/en/references/complex-svg.md | 17 +- .../svg-sprites/src/en/references/legacy.md | 132 ---------- .../src/en/references/migration-1.md | 185 ------------- .../svg-sprites/src/en/references/next-app.md | 15 +- .../src/en/references/next-pages.md | 11 +- .../src/en/references/programmatic-api.md | 243 ++---------------- .../src/en/references/react-vite.md | 31 +-- .../src/en/references/react-webpack.md | 19 +- skills/svg-sprites/src/ru/SKILL.md | 1 - .../src/ru/core/00-package-overview.md | 6 +- .../src/ru/core/10-mode-selection.md | 18 +- .../src/ru/core/20-project-inspection.md | 4 +- .../src/ru/core/30-react-next-setup.md | 13 +- .../src/ru/core/40-generated-contract.md | 32 ++- .../src/ru/core/50-usage-and-colors.md | 2 +- .../src/ru/core/60-verification.md | 6 +- .../svg-sprites/src/ru/core/70-diagnostics.md | 13 +- .../src/ru/references/complex-svg.md | 17 +- .../svg-sprites/src/ru/references/legacy.md | 132 ---------- .../src/ru/references/migration-1.md | 185 ------------- .../svg-sprites/src/ru/references/next-app.md | 15 +- .../src/ru/references/next-pages.md | 11 +- .../src/ru/references/programmatic-api.md | 243 ++---------------- .../src/ru/references/react-vite.md | 29 ++- .../src/ru/references/react-webpack.md | 17 +- 55 files changed, 618 insertions(+), 2168 deletions(-) delete mode 100644 docs/en/legacy.md delete mode 100644 docs/en/migration-1.md delete mode 100644 docs/ru/legacy.md delete mode 100644 docs/ru/migration-1.md delete mode 100644 skills/svg-sprites/src/en/references/legacy.md delete mode 100644 skills/svg-sprites/src/en/references/migration-1.md delete mode 100644 skills/svg-sprites/src/ru/references/legacy.md delete mode 100644 skills/svg-sprites/src/ru/references/migration-1.md diff --git a/FEATURES.md b/FEATURES.md index ebe71d5..9207255 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -42,9 +42,9 @@ `SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. Здесь же доступны готовые примеры подключения через React, ``, `` и CSS. -11. **От нативного HTML до Next.js** +11. **React и Next.js** - В основе остаётся обычный SVG-спрайт, который можно использовать даже без фреймворка и сборщика. Для React и Next.js пакет генерирует типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. Список готовых интеграций будет расширяться новыми фреймворками. + Пакет генерирует типизированные React-компоненты и поддерживает Vite, Webpack 5, Next.js App Router и Pages Router с Turbopack или Webpack. 12. **Чистый Git** diff --git a/README.md b/README.md index d98f873..b1b024a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` is an SVG sprite generator for modern web applications. It combines selected SVG icons into one or more external, cacheable sprites and prepares them for use in the UI. -For React and Next.js, the package generates typed components and supports Vite, Webpack 5, and Turbopack. At its core is a standard SVG sprite that can be used without a framework, including in native HTML. +For React and Next.js, the package generates typed components and external SVG assets with support for Vite, Webpack 5, and Turbopack. ## An SVG sprite as simple as a regular SVG icon @@ -61,9 +61,10 @@ Create the sprite configuration: ```ts // src/ui/app-icons/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'app', inputFiles: [ '../../assets/icons/search.svg', @@ -78,7 +79,7 @@ export default defineNextSpriteConfig({ ```json { "scripts": { - "sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons", + "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites" } @@ -224,11 +225,9 @@ It also provides ready-to-use integration examples for: The Viewer is added only to an internal debug page and does not become part of the generated icon components. -## From native HTML to Next.js +## React and Next.js -At its core is a standard SVG sprite that can be used even without a framework or bundler. - -For React and Next.js, the package generates typed components and supports Vite, Webpack 5, and Turbopack. The list of ready-made integrations will expand to include new frameworks. +The package generates typed React components and supports Vite, Webpack 5, Next.js App Router, and Pages Router with Turbopack or Webpack. ## Clean Git history @@ -252,13 +251,11 @@ This README introduces the project's capabilities and demonstrates the primary u - [Next.js Pages Router](docs/en/next-pages.md) - [React + Vite](docs/en/react-vite.md) - [React + Webpack 5](docs/en/react-webpack.md) -- [Native HTML and classic SVG sprites](docs/en/legacy.md) ### Technical resources - [Technical reference](docs/en/reference.md) - [Programmatic API](docs/en/programmatic-api.md) -- [Migrating from 0.1.x](docs/en/migration-1.md) ## License diff --git a/README_RU.md b/README_RU.md index 47b7f9b..8216cf9 100644 --- a/README_RU.md +++ b/README_RU.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` — генератор SVG-спрайтов для современных веб-приложений. Он собирает выбранные SVG-иконки в один или несколько внешних кешируемых спрайтов и подготавливает их для использования в интерфейсе. -Для React и Next.js пакет создаёт типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. В основе при этом остаётся обычный SVG-спрайт, который можно использовать без фреймворка, в том числе в нативном HTML. +Для React и Next.js пакет создаёт типизированные компоненты и внешние SVG assets, поддерживая Vite, Webpack 5 и Turbopack. ## SVG-спрайт так же прост, как обычная SVG-иконка @@ -61,9 +61,10 @@ src/ ```ts // src/ui/app-icons/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'app', inputFiles: [ '../../assets/icons/search.svg', @@ -78,7 +79,7 @@ export default defineNextSpriteConfig({ ```json { "scripts": { - "sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons", + "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites" } @@ -224,11 +225,9 @@ JavaScript отвечает за интерфейс и поведение, а г Viewer подключается только к внутренней debug-странице и не становится частью generated-компонентов иконок. -## От нативного HTML до Next.js +## React и Next.js -В основе остаётся обычный SVG-спрайт, который можно использовать даже без фреймворка и сборщика. - -Для React и Next.js пакет генерирует типизированные компоненты и поддерживает Vite, Webpack 5 и Turbopack. Список готовых интеграций будет расширяться новыми фреймворками. +Пакет генерирует типизированные React-компоненты и поддерживает Vite, Webpack 5, Next.js App Router и Pages Router с Turbopack или Webpack. ## Чистый Git @@ -252,13 +251,11 @@ README знакомит с возможностями проекта и пока - [Next.js Pages Router](docs/ru/next-pages.md) - [React + Vite](docs/ru/react-vite.md) - [React + Webpack 5](docs/ru/react-webpack.md) -- [Нативный HTML и классические SVG-спрайты](docs/ru/legacy.md) ### Технические материалы - [Технический справочник](docs/ru/reference.md) - [Программный API](docs/ru/programmatic-api.md) -- [Миграция с 0.1.x](docs/ru/migration-1.md) ## Лицензия diff --git a/docs/en/legacy.md b/docs/en/legacy.md deleted file mode 100644 index eea129d..0000000 --- a/docs/en/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Back to home](../../README.md) - -A quick guide to generating centralized SVG sprites in `symbol` and `stack` formats, with an optional HTML preview. - -## 1. Install the package - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Prepare the icons and config - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Add generation - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -Run the local package through the script: - -```bash -npm run sprites -``` - -Result: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -With `preview: false`, the HTML file is not created. For the `stack` format, specify `format: 'stack'`. - -## 4. Use the symbol sprite - -```html - - - -``` - -## Multiple sprites - -Add multiple entries to `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -All output files and the shared `preview.html` will be written to `output`. - -## Troubleshooting - -- Config not found: make sure `svg-sprites.config.ts` is located in the specified root directory. -- No icons: check `sprites[].input` and the `.svg` extension. -- Preview not needed: set `preview: false`. - -For programmatic use, see [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/docs/en/migration-1.md b/docs/en/migration-1.md deleted file mode 100644 index 01c6eb2..0000000 --- a/docs/en/migration-1.md +++ /dev/null @@ -1,122 +0,0 @@ -# Migrating from 0.1.x to 1.0 - -[← Back to home](../../README.md) - -Version 1.0 separates local generation for React and Next.js from the centralized legacy mode. The old config cannot be mixed with the new API in a single CLI invocation. - -## Installation - -Install the package as a development dependency so the migration uses the version recorded in the project lockfile: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## CLI - -The CLI now always requires an explicit `--mode` and a path to the configuration directory: - -```text -"sprites": "svg-sprites" -→ "sprites": "svg-sprites --mode " -``` - -Choose a mode based on your environment: - -| Environment | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Centralized legacy setup | `legacy` | - -## React and Next.js - -Instead of a root-level `svg-sprites.config.ts`, create a local `svg-sprite.config.ts` next to the icon set: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -For regular React, use `defineReactSpriteConfig`. A folder and an explicit list of shared SVG files can be combined using `inputFolder` and `inputFiles`. - -Add the local CLI with the selected mode to `package.json`, for example: - -```json -{ - "scripts": { - "sprite:global": "svg-sprites --mode next@app/turbopack src/ui/global/svg-sprite" - } -} -``` - -Run it with `npm run sprite:global` before importing the generated component. - -The old `publicPath` and `react` options are no longer needed. The generated module is created next to the config and adds its own `.gitignore`, while Vite, Webpack, or Next.js emits the SVG as a separate asset with a content hash. - -The `` component is replaced by a component whose name is derived from `name`: - -```tsx - -``` - -To browse the icons, add `` as a debug page in the application. A separate `preview.html` is available only in legacy mode. - -## Legacy mode - -If you need to preserve the centralized structure, rename the helper and the format fields: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` has been replaced with `defineLegacyConfig`; -- `sprites[].mode` has been renamed to `sprites[].format`; -- `generate` has been replaced with `generateLegacy`; -- `loadConfig` has been replaced with `loadLegacyConfig`; -- `publicPath` and generation of the old shared React component have been removed. - -Add the local CLI to `package.json`: - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy ." - } -} -``` - -Run it with `npm run sprites`. - -## Programmatic API - -The package is distributed as ESM only. Replace `require()` with `import`. - -`compileSpriteContent` now returns `Promise` so that the public declarations do not require `@types/node` to be installed. In Node.js, the actual result is compatible with APIs that accept `Uint8Array`. - -## After migration - -1. Add an explicit generation command before `dev`, `build`, and `typecheck`. -2. Generate the new output and run type checking while the old artifacts are still available. -3. Replace imports and verify the icons and color variables using `SpriteViewer` or the legacy `preview.html`. -4. Only then remove confirmed old generated files and obsolete ignore rules without deleting source SVGs. diff --git a/docs/en/next-app.md b/docs/en/next-app.md index 882d541..1118741 100644 --- a/docs/en/next-app.md +++ b/docs/en/next-app.md @@ -22,19 +22,28 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'file-manager', description: 'File manager icons', }) ``` +The root barrel is application-owned: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + ## 3. Add generation For Turbopack: @@ -42,7 +51,7 @@ For Turbopack: ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager" } @@ -85,7 +94,7 @@ The viewer is interactive, so it requires a separate Client Component boundary: import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), + () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export default function SpritesPage() { diff --git a/docs/en/next-pages.md b/docs/en/next-pages.md index 7620b04..20cfc11 100644 --- a/docs/en/next-pages.md +++ b/docs/en/next-pages.md @@ -22,25 +22,34 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@pages/webpack', name: 'file-manager', description: 'File manager icons', }) ``` +The root barrel is application-owned: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + ## 3. Add generation ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager" } @@ -77,7 +86,7 @@ The component works the same way with SSR, SSG, and client-side navigation. Next import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), + () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export default function SpritesPage() { diff --git a/docs/en/programmatic-api.md b/docs/en/programmatic-api.md index 7a4d44b..b86961c 100644 --- a/docs/en/programmatic-api.md +++ b/docs/en/programmatic-api.md @@ -2,100 +2,65 @@ [← Back to home](../../README.md) -The package provides a main Node.js entry point and a separate React runtime entry point. Both are distributed as ESM only and must be loaded with `import`. +The package is ESM-only and provides one Node.js generation API. The React runtime with `SpriteViewer` is available from the separate `@gromlab/svg-sprites/react` entry point. -To resolve `@gromlab/svg-sprites/react` in TypeScript, use `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -## Main entry point +## `generateSprite` ```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` +import { generateSprite } from '@gromlab/svg-sprites' -The main entry point does not import React and can be used in CLIs, build scripts, and Node.js tools. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', +const result = await generateSprite( + 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', ) ``` -The second argument is required: +The first argument accepts the full path to an explicitly selected `.ts`, `.js`, or `.json` config file with any name. Passing a directory enables config-less mode and uses that directory as the sprite module root. + +The second argument contains optional overrides and always takes precedence over the config: ```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Result: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -The function loads `svg-sprite.config.ts` from the specified root, compiles the SVG files, and safely updates managed files. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', +await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { + mode: 'react@webpack', + name: 'documents', + inputFolder: './assets', + inputFiles: ['../../shared/search.svg'], + transform: { + addTransition: false, }, -) + generatedNotice: false, +}) ``` -Available values: +Configuration is resolved in this order: -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} +```text +defaults → config → API overrides ``` -The result also contains the selected `router`, `bundler`, and the full target in the form `next@app/turbopack`. - -## `defineReactSpriteConfig` +For fully programmatic generation, pass a directory and provide the required settings as overrides: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +await generateSprite('src/ui/file-manager/svg-sprite', { + mode: 'react@vite', + name: 'file-manager', + inputFiles: [ + '../../shared/search.svg', + '../../shared/settings.svg', + ], +}) +``` -export default defineReactSpriteConfig({ +## Configuration + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], + inputFiles: ['../../shared/check.svg'], transform: { removeSize: true, replaceColors: true, @@ -105,99 +70,54 @@ export default defineReactSpriteConfig({ }) ``` -`inputFolder` and `inputFiles` are combined. The helper returns the configuration without runtime transformations and provides TypeScript autocomplete. +`defineSpriteConfig` is an identity helper for TypeScript autocomplete. JavaScript can export the same object with `export default`, while JSON contains the object directly. -## `defineNextSpriteConfig` +## Specialized wrappers + +The specialized functions are available as wrappers around `generateSprite`: ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', +await generateReactSprite('path/to/config.ts', 'vite') +await generateNextSprite('path/to/config.ts', { + router: 'app', + bundler: 'turbopack', }) ``` -Next.js uses the same configuration contract as the React presets. +An explicitly supplied target overrides `mode` from the file. Prefer `generateSprite` in new code. -## `generateLegacy` +## Config API ```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) +import { + loadSpriteConfig, + resolveSpriteConfig, + validateSpriteConfig, +} from '@gromlab/svg-sprites' ``` -Returns an array: +- `loadSpriteConfig(file)` loads an explicitly selected `.ts`, `.js`, or `.json` file. +- `validateSpriteConfig(value)` performs runtime validation. +- `resolveSpriteConfig(root, config, overrides)` merges values, applies defaults, and resolves paths relative to `root`. -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -For details, see [Legacy mode](legacy.md). - -## Low-level functions - -The main entry point also exports: +## Low-level compiler ```ts import { compileSprite, compileSpriteContent, createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, } from '@gromlab/svg-sprites' ``` -These functions are intended for custom orchestration built on top of the existing compiler and writer. For standard usage, prefer `generateReactSprite` and `generateLegacy`. +These functions are intended for custom orchestration. Standard generation should use `generateSprite`. -## React runtime entry point +## React runtime ```tsx import { SpriteViewer } from '@gromlab/svg-sprites/react' ``` -Types: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -The React entry point contains `'use client'` and is intended for debug tools. Generated production components are imported from the application's local sprite modules, not from the package's React entry point. - -`SpriteViewerProps.colorTheme` accepts `auto | light | dark`. The default is `auto`, which follows `prefers-color-scheme`; to synchronize it with the application theme, pass the computed `light` or `dark` value. - -## Related guides - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) +`SpriteViewer` accepts generated manifests, lazy loaders, or an `import.meta.glob` result. This entry point contains `'use client'` and is intended for debug tools; production components are imported from local sprite modules. diff --git a/docs/en/react-vite.md b/docs/en/react-vite.md index 9d00808..4f882a5 100644 --- a/docs/en/react-vite.md +++ b/docs/en/react-vite.md @@ -19,6 +19,7 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` @@ -28,24 +29,32 @@ Place the source SVG files in `icons/`. ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'File manager icons', }) ``` +The root barrel is application-owned and explicitly re-exports the generated API: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. -The complete list of options is available under [React and Next.js configuration](reference.md#react-and-nextjs-configuration). +The complete list of options is available under [Unified configuration](reference.md#unified-configuration). ## 4. Add generation to package.json ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -96,7 +105,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export const IconsDebugPage = () => ( @@ -104,13 +113,13 @@ export const IconsDebugPage = () => ( ) ``` -Vite automatically finds the generated `manifest.ts` for each React sprite. The `import.meta.glob` pattern must be a string literal, and generation must run before Vite starts. +Vite automatically finds the generated manifest for each React sprite. The `import.meta.glob` pattern must be a string literal, and generation must run before Vite starts. Only include the Viewer on a debug route or in an internal tool. ## Troubleshooting -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer cannot find the sprite: check the glob path and make sure `manifest.ts` exists. +- Missing `.svg-sprite/index.js`: run `npm run sprite:file-manager`. +- The Viewer cannot find the sprite: check the glob path to `.svg-sprite/svg-sprite.manifest.js`. - `Refusing to overwrite a user file` error: there is a user file at a generated path. - The icon does not change color: use `color` or `--icon-color-N`. diff --git a/docs/en/react-webpack.md b/docs/en/react-webpack.md index a33a234..1d1926c 100644 --- a/docs/en/react-webpack.md +++ b/docs/en/react-webpack.md @@ -19,6 +19,7 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` @@ -28,24 +29,32 @@ Place the source SVG files in `icons/`. ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@webpack', name: 'file-manager', description: 'File manager icons', }) ``` +The root barrel is application-owned and explicitly re-exports the generated API: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. -The complete list of options is available under [React and Next.js configuration](reference.md#react-and-nextjs-configuration). +The complete list of options is available under [Unified configuration](reference.md#unified-configuration). ## 4. Add generation to package.json ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -87,7 +96,7 @@ Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` throu If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules. -The generated component imports `styles.module.css`, so Webpack must process CSS Modules through `css-loader` and `style-loader` or `MiniCssExtractPlugin`. If the TypeScript project does not include a declaration for CSS Modules, add one separately. +The generated component imports `react/react-component.module.css`, so Webpack must process CSS Modules through `css-loader` and `style-loader` or `MiniCssExtractPlugin`. If the TypeScript project does not include a declaration for CSS Modules, add one separately. ## 6. Add a debug page @@ -97,8 +106,8 @@ Webpack does not support Vite's `import.meta.glob` API, so provide static loader import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), + () => import('./ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('./ui/navigation/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export const IconsDebugPage = () => ( @@ -112,8 +121,8 @@ Only include the Viewer on a debug route or in an internal tool. ## Troubleshooting -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer does not load the sprite: check the path in `import()` and make sure `manifest.ts` exists. +- Missing `.svg-sprite/index.js`: run `npm run sprite:file-manager`. +- The Viewer does not load the sprite: check the `import()` path to `.svg-sprite/svg-sprite.manifest.js`. - Incorrect asset URL: check `output.publicPath`. - Another loader intercepts the SVG: exclude the generated sprite from the incompatible rule. diff --git a/docs/en/reference.md b/docs/en/reference.md index 61a3b1c..9ffe21d 100644 --- a/docs/en/reference.md +++ b/docs/en/reference.md @@ -8,7 +8,6 @@ Reference for the configuration, generated API, and behavior of `@gromlab/svg-sp - [Next.js Pages Router](next-pages.md) - [React + Vite](react-vite.md) - [React + Webpack 5](react-webpack.md) -- [Native HTML and classic SVG sprites](legacy.md) ## Requirements @@ -25,10 +24,10 @@ npm install --save-dev @gromlab/svg-sprites ## CLI and generation modes -The CLI accepts one mode and a path to the configuration directory: +The CLI accepts exactly one path: an explicitly selected config file or a directory for config-less generation: ```text -svg-sprites --mode +svg-sprites [options] ``` | Environment | Mode | @@ -39,20 +38,24 @@ svg-sprites --mode | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | | Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Classic `stack` and `symbol` sprites | `legacy` | -Modern React and Next.js modes use a local `svg-sprite.config.ts`. Legacy mode uses a separate `svg-sprites.config.ts` and is covered in [its own guide](legacy.md). +The config file may have any name and use the `.ts`, `.js`, or `.json` extension. The CLI does not discover it by convention: pass the file explicitly. The guides use `svg-sprite.config.ts` as the recommended name. + +When a directory is passed, all settings come from CLI options. When a config file is passed, CLI options override the file. The full order is `defaults → config → CLI`. + +Available options are `--mode`, `--name`, `--description`, `--input-folder`, repeatable `--input-file`, plus the `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition`, and `--generated-notice`/`--no-generated-notice` pairs. Transform flags override individual fields, while supplying at least one `--input-file` replaces the complete config `inputFiles` array. The mode must match the application's bundler. The generator creates different SVG asset integration code for Vite and for bundlers compatible with Webpack Asset Modules. -## React and Next.js configuration +## Unified configuration -Each directory containing `svg-sprite.config.ts` defines one independent sprite. +Each config file defines one independent sprite. ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'app', description: 'Shared application icons', inputFolder: './local-icons', @@ -69,18 +72,13 @@ export default defineNextSpriteConfig({ }) ``` -For React, use `defineReactSpriteConfig`. The configuration contract is the same: - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' -``` - | Option | Type | Default | Purpose | |---|---|---|---| +| `mode` | `SpriteMode` | None | Generation mode; may be supplied by CLI/API | | `name` | `string` | Derived from the directory | Name of the sprite, component, and public types | | `description` | `string` | None | Description for types and the debug manifest | -| `inputFolder` | `string` | `./icons` | SVG directory relative to the configuration file | -| `inputFiles` | `string[]` | `[]` | Paths to individual SVG files relative to the configuration file | +| `inputFolder` | `string` | `./icons` | SVG directory relative to the module root | +| `inputFiles` | `string[]` | `[]` | Paths to individual SVG files relative to the module root | | `transform` | `TransformOptions` | All enabled | SVG preparation settings | | `generatedNotice` | `boolean` | `true` | Full or abbreviated warning in generated files | @@ -112,28 +110,41 @@ After generation, the sprite directory looks like this: ```text app-icons/ ├── .gitignore -├── index.ts -├── manifest.ts ├── svg-sprite.config.ts -└── generated/ - ├── .svg-sprites.manifest.json - ├── react-component.tsx +├── index.ts # optional user-owned barrel +└── .svg-sprite/ + ├── state.json + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts ├── sprite.svg - ├── styles.module.css - └── types.ts + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css ``` | File | Purpose | |---|---| -| `index.ts` | Production exports for the component, props, styles, and icon names | -| `manifest.ts` | Debug metadata and the asset URL for `SpriteViewer` | -| `generated/sprite.svg` | Compiled SVG sprite | -| `generated/react-component.tsx` | Typed React component | -| `generated/styles.module.css` | Base styles and transitions | -| `generated/types.ts` | Runtime list and union type of icon names | -| `generated/.svg-sprites.manifest.json` | List of files managed by the generator | +| `.svg-sprite/index.js` | Production exports for the component and runtime icon-name list | +| `.svg-sprite/index.d.ts` | Public declarations for the component, props, styles, and icon-name union | +| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata and the asset URL for `SpriteViewer` | +| `.svg-sprite/sprite.svg` | Compiled SVG sprite | +| `.svg-sprite/react/react-component.js` | React component runtime without TypeScript or JSX | +| `.svg-sprite/react/react-component.d.ts` | React component props, style, and declaration | +| `.svg-sprite/react/react-component.module.css` | Styles for the React implementation | +| `.svg-sprite/icon-data.js` | Runtime icon-name list and internal IDs | +| `.svg-sprite/*.d.ts` | TypeScript declarations for the corresponding JavaScript modules | +| `.svg-sprite/state.json` | Mode, contract version, and managed file list | -The generator overwrites and deletes only files that contain its marker. If a user file occupies a managed path, generation fails. +The generator overwrites and deletes only files that contain its marker. If a user file occupies a managed path, generation fails. The root `index.ts` is user-owned; create a barrel when needed: + +```ts +export * from './.svg-sprite' +``` ## React component and TypeScript @@ -224,12 +235,11 @@ For multiple sprites, add a separate CLI command for each directory or combine t ## Formats and rendering methods -Modern React and Next.js modes generate the `stack` format. Legacy mode supports both `stack` and `symbol`. +React and Next.js modes generate the `stack` format. | Format | `` | `` | CSS background | |---|---:|---:|---:| | `stack` | Yes | Yes | Yes | -| `symbol` | Yes | No | No | ### Generated component @@ -246,13 +256,13 @@ How you obtain `spriteUrl` depends on the bundler. Vite: ```ts -import spriteUrl from './generated/sprite.svg?no-inline' +import spriteUrl from './.svg-sprite/sprite.svg?no-inline' ``` Webpack 5, Turbopack, and Next.js: ```ts -const spriteUrl = new URL('./generated/sprite.svg', import.meta.url).href +const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href ``` After obtaining the URL, use it in JSX: @@ -277,7 +287,7 @@ An SVG inside `` is isolated from the page's CSS. Setting `color` or `--ico ```css .icon { - background: url('./generated/sprite.svg#search') center / contain no-repeat; + background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; } ``` @@ -286,7 +296,7 @@ For a single-color silhouette, you can use a mask: ```css .icon { background-color: currentColor; - mask: url('./generated/sprite.svg#search') center / contain no-repeat; + mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; } ``` @@ -300,7 +310,7 @@ The generated component passes the SVG to the bundler as a separate asset: - Vite uses a static import with `?no-inline`; - Webpack 5, Turbopack, and Next.js use `new URL(..., import.meta.url)`; -- SVG path data is not serialized into the generated TSX. +- SVG path data is not serialized into generated JavaScript. With standard asset naming, the bundler adds a content hash: @@ -325,7 +335,8 @@ All transformations are enabled by default and can be configured independently: To disable an individual operation: ```ts -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', transform: { removeSize: false, replaceColors: false, @@ -399,7 +410,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export const IconsDebugPage = () => ( @@ -411,8 +422,8 @@ Webpack and Next.js: ```tsx const sources = [ - () => import('@/ui/app-icons/manifest'), - () => import('@/features/analytics/icons/manifest'), + () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), + () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), ] export const IconsDebugPage = () => ( @@ -447,9 +458,7 @@ To synchronize it with the application theme: A modern sprite module creates a local `.gitignore` for: ```text -/generated/ -/index.ts -/manifest.ts +/.svg-sprite/ ``` Commit the local `.gitignore` to the repository once. It excludes the other generated files, so generation must run before commands that import the sprite module: @@ -457,7 +466,7 @@ Commit the local `.gitignore` to the repository once. It excludes the other gene ```json { "scripts": { - "sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons", + "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites", "pretypecheck": "npm run sprites" @@ -467,18 +476,19 @@ Commit the local `.gitignore` to the repository once. It excludes the other gene CI must install development dependencies and run the generation script before building or type-checking. -If the sprite directory already contains a user-created `.gitignore`, `index.ts`, or `manifest.ts`, the generator will not overwrite it. Move the user file or choose a separate sprite directory. +If the sprite directory already contains a user-created `.gitignore` or a user-owned file inside `.svg-sprite`, the generator will not overwrite it. The root `index.ts` remains user-owned and may re-export the generated API. ## Troubleshooting -- Missing `index.ts`: run the generation script before importing the module. -- Configuration not found: check the CLI path and the `svg-sprite.config.ts` file name. +- Missing `.svg-sprite/index.js`: run the generation script before importing the generated module. +- Source not found: pass an existing config file or sprite module directory. +- Mode missing: add `mode` to the config or pass `--mode`. - Icon missing from the type: check `inputFiles`, the `.svg` extension, and the nesting level under `inputFolder`. - Name conflict: two different SVG files have the same basename; rename one of them. - `Refusing to overwrite a user file`: a file without the generated marker occupies a managed path. - The icon does not change color: use `` or the generated component and check `replaceColors`. - Webpack emits an incorrect URL: check Asset Modules, `output.publicPath`, and SVG loaders. -- The Viewer cannot find the sprite: check the path to `manifest.ts` and run generation before starting the application. +- The Viewer cannot find the sprite: check the path to `.svg-sprite/svg-sprite.manifest.js` and run generation before starting the application. - Build and mode do not match: use the target that corresponds to the actual bundler. For custom orchestration and low-level compilation, see the [Programmatic API](programmatic-api.md). diff --git a/docs/ru/legacy.md b/docs/ru/legacy.md deleted file mode 100644 index 4e3f36d..0000000 --- a/docs/ru/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Главная](../../README_RU.md) - -Краткая инструкция по генерации централизованных SVG-спрайтов форматов `symbol` и `stack` с optional HTML preview. - -## 1. Установите пакет - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Подготовьте иконки и конфиг - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Добавьте генерацию - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -Запустите локально установленный пакет через script: - -```bash -npm run sprites -``` - -Результат: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -При `preview: false` HTML-файл не создаётся. Для формата `stack` укажите `format: 'stack'`. - -## 4. Используйте symbol-спрайт - -```html - - - -``` - -## Несколько спрайтов - -Добавьте несколько записей в `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -Все результаты и общий `preview.html` будут записаны в `output`. - -## Если что-то не работает - -- Не найден конфиг: убедитесь, что `svg-sprites.config.ts` находится в переданном корне. -- Нет иконок: проверьте `sprites[].input` и расширение `.svg`. -- Не нужен preview: установите `preview: false`. - -Для программного запуска используйте [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/docs/ru/migration-1.md b/docs/ru/migration-1.md deleted file mode 100644 index bf56fa6..0000000 --- a/docs/ru/migration-1.md +++ /dev/null @@ -1,122 +0,0 @@ -# Миграция с 0.1.x на 1.0 - -[← Главная](../../README_RU.md) - -Версия 1.0 разделяет локальную генерацию для React и Next.js и централизованный legacy-режим. Старый config нельзя смешивать с новым API в одном вызове CLI. - -## Установка - -Установите пакет как development dependency, чтобы миграция использовала версию из lockfile проекта: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## CLI - -CLI теперь всегда требует явный `--mode` и путь к каталогу конфигурации: - -```text -"sprites": "svg-sprites" -→ "sprites": "svg-sprites --mode " -``` - -Выберите mode по окружению: - -| Окружение | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Централизованная старая схема | `legacy` | - -## React и Next.js - -Вместо корневого `svg-sprites.config.ts` создайте локальный `svg-sprite.config.ts` рядом с набором иконок: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -Для обычного React используйте `defineReactSpriteConfig`. Папку и явный список общих SVG можно объединить через `inputFolder` и `inputFiles`. - -Добавьте локальный CLI с выбранным mode в `package.json`, например: - -```json -{ - "scripts": { - "sprite:global": "svg-sprites --mode next@app/turbopack src/ui/global/svg-sprite" - } -} -``` - -Запустите его командой `npm run sprite:global` до импорта generated-компонента. - -Старые `publicPath` и `react` больше не нужны. Generated-модуль создаётся рядом с конфигом, сам добавляет `.gitignore`, а Vite, Webpack или Next.js выпускает SVG как отдельный asset с content hash. - -Компонент `` заменяется компонентом, имя которого выводится из `name`: - -```tsx - -``` - -Для просмотра иконок добавьте `` как debug-страницу приложения. Отдельный `preview.html` остаётся только в legacy-режиме. - -## Legacy-режим - -Если централизованную структуру нужно сохранить, переименуйте helper и поля формата: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` заменён на `defineLegacyConfig`; -- `sprites[].mode` переименован в `sprites[].format`; -- `generate` заменён на `generateLegacy`; -- `loadConfig` заменён на `loadLegacyConfig`; -- `publicPath` и генерация старого общего React-компонента удалены. - -Добавьте локальный CLI в `package.json`: - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy ." - } -} -``` - -Запустите его командой `npm run sprites`. - -## Программный API - -Пакет распространяется только как ESM. Замените `require()` на `import`. - -`compileSpriteContent` теперь возвращает `Promise`, чтобы публичные декларации не требовали установки `@types/node`. В Node.js фактический результат совместим с API, принимающими `Uint8Array`. - -## После миграции - -1. Добавьте явную команду генерации перед `dev`, `build` и `typecheck`. -2. Создайте новый output и запустите проверку типов, пока старые artifacts остаются доступны. -3. Замените imports и проверьте иконки и цветовые переменные через `SpriteViewer` или legacy `preview.html`. -4. Только после этого удалите подтверждённые старые generated-файлы и устаревшие ignore rules, не затрагивая исходные SVG. diff --git a/docs/ru/next-app.md b/docs/ru/next-app.md index 94b8260..653b1b6 100644 --- a/docs/ru/next-app.md +++ b/docs/ru/next-app.md @@ -22,19 +22,28 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'file-manager', description: 'Иконки файлового менеджера', }) ``` +Корневой barrel принадлежит приложению: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + ## 3. Добавьте генерацию Для Turbopack: @@ -42,7 +51,7 @@ export default defineNextSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager" } @@ -85,7 +94,7 @@ Viewer интерактивен, поэтому для него нужна от import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), + () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export default function SpritesPage() { diff --git a/docs/ru/next-pages.md b/docs/ru/next-pages.md index 42e2927..bd6526a 100644 --- a/docs/ru/next-pages.md +++ b/docs/ru/next-pages.md @@ -22,25 +22,34 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@pages/webpack', name: 'file-manager', description: 'Иконки файлового менеджера', }) ``` +Корневой barrel принадлежит приложению: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + ## 3. Добавьте генерацию ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager" } @@ -77,7 +86,7 @@ export function getServerSideProps() { import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), + () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export default function SpritesPage() { diff --git a/docs/ru/programmatic-api.md b/docs/ru/programmatic-api.md index 999cd01..b095747 100644 --- a/docs/ru/programmatic-api.md +++ b/docs/ru/programmatic-api.md @@ -2,100 +2,65 @@ [← Главная](../../README_RU.md) -Пакет предоставляет основную Node.js точку входа и отдельный React runtime entry. Обе точки распространяются только как ESM и подключаются через `import`. +Пакет распространяется как ESM и предоставляет единый Node.js API генерации. React runtime с `SpriteViewer` находится в отдельной точке входа `@gromlab/svg-sprites/react`. -Для разрешения `@gromlab/svg-sprites/react` в TypeScript используйте `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -## Основной entry +## `generateSprite` ```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` +import { generateSprite } from '@gromlab/svg-sprites' -Основной entry не импортирует React и может использоваться в CLI, build scripts и Node.js инструментах. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', +const result = await generateSprite( + 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', ) ``` -Второй аргумент обязателен: +Первый аргумент принимает полный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог. + +Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом: ```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Результат: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -Функция загружает `svg-sprite.config.ts` из указанного корня, компилирует SVG и безопасно обновляет managed-файлы. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', +await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { + mode: 'react@webpack', + name: 'documents', + inputFolder: './assets', + inputFiles: ['../../shared/search.svg'], + transform: { + addTransition: false, }, -) + generatedNotice: false, +}) ``` -Доступные значения: +Порядок разрешения настроек: -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} +```text +defaults → config → API overrides ``` -Результат дополнительно содержит выбранные `router`, `bundler` и полный target вида `next@app/turbopack`. - -## `defineReactSpriteConfig` +Для полностью программной генерации передайте каталог и все обязательные настройки через overrides: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +await generateSprite('src/ui/file-manager/svg-sprite', { + mode: 'react@vite', + name: 'file-manager', + inputFiles: [ + '../../shared/search.svg', + '../../shared/settings.svg', + ], +}) +``` -export default defineReactSpriteConfig({ +## Конфигурация + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], + inputFiles: ['../../shared/check.svg'], transform: { removeSize: true, replaceColors: true, @@ -105,99 +70,54 @@ export default defineReactSpriteConfig({ }) ``` -`inputFolder` и `inputFiles` объединяются. Хелпер возвращает конфиг без runtime-преобразований и предоставляет TypeScript autocomplete. +`defineSpriteConfig` является identity helper для TypeScript autocomplete. JS может экспортировать тот же объект через `export default`, а JSON содержит объект непосредственно. -## `defineNextSpriteConfig` +## Специализированные обёртки + +Специализированные функции доступны как обёртки над `generateSprite`: ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', +await generateReactSprite('path/to/config.ts', 'vite') +await generateNextSprite('path/to/config.ts', { + router: 'app', + bundler: 'turbopack', }) ``` -Next.js использует тот же контракт конфигурации, что и React presets. +Явно переданный target перекрывает `mode` из файла. Для нового кода используйте `generateSprite`. -## `generateLegacy` +## Config API ```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) +import { + loadSpriteConfig, + resolveSpriteConfig, + validateSpriteConfig, +} from '@gromlab/svg-sprites' ``` -Возвращается массив: +- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл. +- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта. +- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`. -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -Подробнее: [Legacy mode](legacy.md). - -## Низкоуровневые функции - -Основная точка входа также экспортирует: +## Низкоуровневый compiler ```ts import { compileSprite, compileSpriteContent, createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, } from '@gromlab/svg-sprites' ``` -Эти функции предназначены для собственного orchestration поверх существующего compiler и writer. Для стандартного использования предпочтительны `generateReactSprite` и `generateLegacy`. +Эти функции предназначены для собственного orchestration. Стандартная генерация должна выполняться через `generateSprite`. -## React runtime entry +## React runtime ```tsx import { SpriteViewer } from '@gromlab/svg-sprites/react' ``` -Типы: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -React entry содержит `'use client'` и предназначен для debug-инструментов. Generated production-компоненты импортируются из локальных sprite-модулей приложения, а не из React entry пакета. - -`SpriteViewerProps.colorTheme` принимает `auto | light | dark`. Значение `auto` используется по умолчанию и следует `prefers-color-scheme`; для синхронизации с темой приложения передавайте вычисленное `light` или `dark`. - -## Связанные руководства - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) +`SpriteViewer` принимает generated manifests, lazy loaders или результат `import.meta.glob`. Эта точка входа содержит `'use client'` и предназначена для debug-инструментов; production-компоненты импортируются из локальных sprite-модулей приложения. diff --git a/docs/ru/react-vite.md b/docs/ru/react-vite.md index f1c99ab..59b7bb0 100644 --- a/docs/ru/react-vite.md +++ b/docs/ru/react-vite.md @@ -19,6 +19,7 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` @@ -28,24 +29,32 @@ src/ui/file-manager/svg-sprite/ ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'Иконки файлового менеджера', }) ``` +Корневой barrel принадлежит приложению и явно возвращает generated API: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. -Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs). +Полный список опций находится в разделе [«Единая конфигурация»](reference.md#единая-конфигурация). ## 4. Добавьте генерацию в package.json ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -96,7 +105,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export const IconsDebugPage = () => ( @@ -104,13 +113,13 @@ export const IconsDebugPage = () => ( ) ``` -Vite автоматически найдёт generated `manifest.ts` каждого React-спрайта. Шаблон `import.meta.glob` должен быть строковым литералом, а генерация должна выполниться до запуска Vite. +Vite автоматически найдёт generated manifest каждого React-спрайта. Шаблон `import.meta.glob` должен быть строковым литералом, а генерация должна выполниться до запуска Vite. Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. ## Если что-то не работает -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не видит спрайт: проверьте путь glob и наличие `manifest.ts`. +- Нет `.svg-sprite/index.js`: запустите `npm run sprite:file-manager`. +- Viewer не видит спрайт: проверьте glob-путь к `.svg-sprite/svg-sprite.manifest.js`. - Ошибка `Refusing to overwrite a user file`: в generated-пути находится пользовательский файл. - Иконка не меняет цвет: используйте `color` или `--icon-color-N`. diff --git a/docs/ru/react-webpack.md b/docs/ru/react-webpack.md index 567609f..327afba 100644 --- a/docs/ru/react-webpack.md +++ b/docs/ru/react-webpack.md @@ -19,6 +19,7 @@ src/ui/file-manager/svg-sprite/ ├── icons/ │ ├── check.svg │ └── folder.svg +├── index.ts └── svg-sprite.config.ts ``` @@ -28,24 +29,32 @@ src/ui/file-manager/svg-sprite/ ```ts // src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@webpack', name: 'file-manager', description: 'Иконки файлового менеджера', }) ``` +Корневой barrel принадлежит приложению и явно возвращает generated API: + +```ts +// src/ui/file-manager/svg-sprite/index.ts +export * from './.svg-sprite' +``` + По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. -Полный список опций находится в разделе [«Конфигурация React и Next.js»](reference.md#конфигурация-react-и-nextjs). +Полный список опций находится в разделе [«Единая конфигурация»](reference.md#единая-конфигурация). ## 4. Добавьте генерацию в package.json ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -87,7 +96,7 @@ Webpack обработает generated `new URL('./sprite.svg', import.meta.url) Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules. -Generated-компонент импортирует `styles.module.css`, поэтому Webpack должен обрабатывать CSS Modules через `css-loader` и `style-loader` либо `MiniCssExtractPlugin`. Если TypeScript-проект не содержит декларации для CSS Modules, добавьте её отдельно. +Generated-компонент импортирует `react/react-component.module.css`, поэтому Webpack должен обрабатывать CSS Modules через `css-loader` и `style-loader` либо `MiniCssExtractPlugin`. Если TypeScript-проект не содержит декларации для CSS Modules, добавьте её отдельно. ## 6. Добавьте debug-страницу @@ -97,8 +106,8 @@ Webpack не поддерживает Vite API `import.meta.glob`, поэтом import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), + () => import('./ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('./ui/navigation/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), ] export const IconsDebugPage = () => ( @@ -112,8 +121,8 @@ export const IconsDebugPage = () => ( ## Если что-то не работает -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не загружает спрайт: проверьте путь в `import()` и наличие `manifest.ts`. +- Нет `.svg-sprite/index.js`: запустите `npm run sprite:file-manager`. +- Viewer не загружает спрайт: проверьте путь в `import()` к `.svg-sprite/svg-sprite.manifest.js`. - Неверный URL asset: проверьте `output.publicPath`. - SVG перехватывает другой loader: исключите generated sprite из несовместимого правила. diff --git a/docs/ru/reference.md b/docs/ru/reference.md index 218c67b..8115f2f 100644 --- a/docs/ru/reference.md +++ b/docs/ru/reference.md @@ -8,7 +8,6 @@ - [Next.js Pages Router](next-pages.md) - [React + Vite](react-vite.md) - [React + Webpack 5](react-webpack.md) -- [Нативный HTML и классические SVG-спрайты](legacy.md) ## Требования @@ -25,10 +24,10 @@ npm install --save-dev @gromlab/svg-sprites ## CLI и режимы генерации -CLI принимает один режим и путь к каталогу конфигурации: +CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации: ```text -svg-sprites --mode +svg-sprites [options] ``` | Среда | Mode | @@ -39,20 +38,24 @@ svg-sprites --mode | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | | Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Классические `stack`- и `symbol`-спрайты | `legacy` | -Современные React- и Next.js-режимы используют локальный `svg-sprite.config.ts`. Legacy-режим использует отдельный `svg-sprites.config.ts` и описан в [собственном руководстве](legacy.md). +Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.ts`. + +Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`. + +Доступны `--mode`, `--name`, `--description`, `--input-folder`, повторяемый `--input-file`, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input-file` заменяет весь массив `inputFiles` из config. Mode должен соответствовать сборщику приложения. Генератор создаёт разный способ подключения SVG asset для Vite и сборщиков, совместимых с Webpack Asset Modules. -## Конфигурация React и Next.js +## Единая конфигурация -Каждый каталог с `svg-sprite.config.ts` описывает один независимый спрайт. +Каждый config-файл описывает один независимый спрайт. ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'app', description: 'Общие иконки приложения', inputFolder: './local-icons', @@ -69,18 +72,13 @@ export default defineNextSpriteConfig({ }) ``` -Для React используйте `defineReactSpriteConfig`. Контракт конфигурации одинаковый: - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' -``` - | Опция | Тип | По умолчанию | Назначение | |---|---|---|---| +| `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API | | `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов | | `description` | `string` | Нет | Описание для типов и debug manifest | -| `inputFolder` | `string` | `./icons` | Каталог с SVG относительно конфига | -| `inputFiles` | `string[]` | `[]` | Пути к отдельным SVG относительно конфига | +| `inputFolder` | `string` | `./icons` | Каталог с SVG относительно корня модуля | +| `inputFiles` | `string[]` | `[]` | Пути к отдельным SVG относительно корня модуля | | `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG | | `generatedNotice` | `boolean` | `true` | Полное или короткое предупреждение в generated-файлах | @@ -112,28 +110,41 @@ file-manager → FileManagerIcon ```text app-icons/ ├── .gitignore -├── index.ts -├── manifest.ts ├── svg-sprite.config.ts -└── generated/ - ├── .svg-sprites.manifest.json - ├── react-component.tsx +├── index.ts # необязательный пользовательский barrel +└── .svg-sprite/ + ├── state.json + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts ├── sprite.svg - ├── styles.module.css - └── types.ts + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css ``` | Файл | Назначение | |---|---| -| `index.ts` | Production exports компонента, props, стилей и имён иконок | -| `manifest.ts` | Debug metadata и URL asset для `SpriteViewer` | -| `generated/sprite.svg` | Собранный SVG-спрайт | -| `generated/react-component.tsx` | Типизированный React-компонент | -| `generated/styles.module.css` | Базовые стили и transitions | -| `generated/types.ts` | Runtime-список и union-тип имён | -| `generated/.svg-sprites.manifest.json` | Список файлов, которыми управляет генератор | +| `.svg-sprite/index.js` | Production exports компонента и runtime-списка имён | +| `.svg-sprite/index.d.ts` | Публичные декларации компонента, props, стилей и union-типа имён | +| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata и URL asset для `SpriteViewer` | +| `.svg-sprite/sprite.svg` | Собранный SVG-спрайт | +| `.svg-sprite/react/react-component.js` | Runtime React-компонента без TypeScript и JSX | +| `.svg-sprite/react/react-component.d.ts` | Props, style и declaration React-компонента | +| `.svg-sprite/react/react-component.module.css` | Стили конкретной React-реализации | +| `.svg-sprite/icon-data.js` | Runtime-список имён и внутренние IDs | +| `.svg-sprite/*.d.ts` | TypeScript-декларации соответствующих JS-модулей | +| `.svg-sprite/state.json` | Mode, версия контракта и список управляемых файлов | -Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. +Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой `index.ts` генератору не принадлежит; при необходимости создайте пользовательский barrel: + +```ts +export * from './.svg-sprite' +``` ## React-компонент и TypeScript @@ -224,12 +235,11 @@ editor-icons → EditorIcon → иконки редактора ## Форматы и способы отображения -Современные React- и Next.js-режимы создают формат `stack`. Legacy-режим поддерживает `stack` и `symbol`. +React- и Next.js-режимы создают формат `stack`. | Формат | `` | `` | CSS background | |---|---:|---:|---:| | `stack` | Да | Да | Да | -| `symbol` | Да | Нет | Нет | ### Generated-компонент @@ -246,13 +256,13 @@ editor-icons → EditorIcon → иконки редактора Vite: ```ts -import spriteUrl from './generated/sprite.svg?no-inline' +import spriteUrl from './.svg-sprite/sprite.svg?no-inline' ``` Webpack 5, Turbopack и Next.js: ```ts -const spriteUrl = new URL('./generated/sprite.svg', import.meta.url).href +const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href ``` После получения URL используйте его в JSX: @@ -277,7 +287,7 @@ SVG внутри `` изолирован от CSS страницы. `color` ```css .icon { - background: url('./generated/sprite.svg#search') center / contain no-repeat; + background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; } ``` @@ -286,7 +296,7 @@ SVG внутри `` изолирован от CSS страницы. `color` ```css .icon { background-color: currentColor; - mask: url('./generated/sprite.svg#search') center / contain no-repeat; + mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; } ``` @@ -300,7 +310,7 @@ Generated-компонент передаёт SVG сборщику как отд - Vite использует статический импорт с `?no-inline`; - Webpack 5, Turbopack и Next.js используют `new URL(..., import.meta.url)`; -- SVG path-данные не сериализуются в generated TSX. +- SVG path-данные не сериализуются в generated JavaScript. При стандартном именовании assets сборщик добавляет content hash: @@ -325,7 +335,8 @@ HTTP cache headers, CDN и `Cache-Control` настраиваются прило Чтобы отключить отдельную операцию: ```ts -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', transform: { removeSize: false, replaceColors: false, @@ -399,7 +410,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export const IconsDebugPage = () => ( @@ -411,8 +422,8 @@ Webpack и Next.js: ```tsx const sources = [ - () => import('@/ui/app-icons/manifest'), - () => import('@/features/analytics/icons/manifest'), + () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), + () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), ] export const IconsDebugPage = () => ( @@ -447,9 +458,7 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем Современный sprite-модуль создаёт локальный `.gitignore` для: ```text -/generated/ -/index.ts -/manifest.ts +/.svg-sprite/ ``` Локальный `.gitignore` следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль: @@ -457,7 +466,7 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем ```json { "scripts": { - "sprites": "svg-sprites --mode next@app/turbopack src/ui/app-icons", + "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites", "pretypecheck": "npm run sprites" @@ -467,18 +476,19 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем CI должен устанавливать development dependencies и выполнять generation script до сборки или проверки типов. -Если в каталоге спрайта уже находится пользовательский `.gitignore`, `index.ts` или `manifest.ts`, генератор не перезапишет его. Переместите пользовательский файл или выберите отдельный каталог спрайта. +Если в каталоге спрайта уже находится пользовательский `.gitignore` либо пользовательский файл внутри `.svg-sprite`, генератор не перезапишет его. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API. ## Диагностика -- Нет `index.ts`: запустите generation script до импорта модуля. -- Не найдена конфигурация: проверьте путь CLI и имя `svg-sprite.config.ts`. +- Нет `.svg-sprite/index.js`: запустите generation script до импорта generated-модуля. +- Не найден источник: передайте существующий config-файл или каталог sprite-модуля. +- Не указан mode: добавьте `mode` в config либо передайте `--mode`. - Иконка отсутствует в типе: проверьте `inputFiles`, расширение `.svg` и уровень вложенности `inputFolder`. - Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл. - `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker. - Иконка не меняет цвет: используйте `` или generated-компонент и проверьте `replaceColors`. - Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders. -- Viewer не видит спрайт: проверьте путь к `manifest.ts` и выполните генерацию до запуска приложения. +- Viewer не видит спрайт: проверьте путь к `.svg-sprite/svg-sprite.manifest.js` и выполните генерацию до запуска приложения. - Build и mode не совпадают: используйте target, соответствующий фактическому сборщику. Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md). diff --git a/skills/README.md b/skills/README.md index 42d3a6b..5c7c978 100644 --- a/skills/README.md +++ b/skills/README.md @@ -21,8 +21,6 @@ src// ├── react-webpack.md ├── next-app.md ├── next-pages.md - ├── legacy.md - ├── migration-1.md ├── programmatic-api.md └── complex-svg.md ``` diff --git a/skills/svg-sprites/skill.config.mjs b/skills/svg-sprites/skill.config.mjs index 2be3967..5f2bacf 100644 --- a/skills/svg-sprites/skill.config.mjs +++ b/skills/svg-sprites/skill.config.mjs @@ -3,8 +3,6 @@ const agentReferences = [ 'react-webpack.md', 'next-app.md', 'next-pages.md', - 'legacy.md', - 'migration-1.md', 'programmatic-api.md', 'complex-svg.md', ] @@ -37,7 +35,7 @@ const upstream = [ export default [ { name: 'svg-sprites', - description: 'Use only when configuring, generating, migrating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, defineLegacyConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', + description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', output: '../artifacts/svg-sprites', maxSkillBytes: 48_000, documents: documents('en'), @@ -45,7 +43,7 @@ export default [ }, { name: 'svg-sprites-ru', - description: 'Используй только при настройке, изменении, миграции или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, defineLegacyConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', + description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', output: '../artifacts/svg-sprites-ru', maxSkillBytes: 48_000, documents: documents('ru'), diff --git a/skills/svg-sprites/src/en/SKILL.md b/skills/svg-sprites/src/en/SKILL.md index 81b117a..6211008 100644 --- a/skills/svg-sprites/src/en/SKILL.md +++ b/skills/svg-sprites/src/en/SKILL.md @@ -13,7 +13,6 @@ - For React + Vite, open [React + Vite](./references/react-vite.md); for React with a custom Webpack 5 setup, open [React + Webpack](./references/react-webpack.md). - For Next.js, open the guide for the [App Router](./references/next-app.md) or [Pages Router](./references/next-pages.md), then select the section for the bundler actually in use. -- If the project has `svg-sprites.config.ts`, open [legacy mode](./references/legacy.md); when upgrading from `0.1.x`, also open the [migration guide](./references/migration-1.md). - To invoke the generator from Node.js, open the [programmatic API](./references/programmatic-api.md). - For gradients, filters, `url(#...)`, unusual colors, and `viewBox` issues, open [complex SVGs](./references/complex-svg.md). - Use the complete user documentation as a secondary source: [package README](./references/upstream/README.md). diff --git a/skills/svg-sprites/src/en/core/00-package-overview.md b/skills/svg-sprites/src/en/core/00-package-overview.md index 9df8749..8f37578 100644 --- a/skills/svg-sprites/src/en/core/00-package-overview.md +++ b/skills/svg-sprites/src/en/core/00-package-overview.md @@ -2,13 +2,13 @@ `@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external cacheable sprite asset and, for React/Next.js, creates a typed component, a list of valid names, and a debug manifest. -The package supports multiple independent sprites in one project. Each selected directory containing `svg-sprite.config.ts` describes one sprite and gets its own: +The package supports multiple independent sprites in one project. Each explicitly selected config file or config-less directory describes one sprite and gets its own: - SVG asset; - icon name types; - React component; -- production entry point `index.ts`; -- debug entry point `manifest.ts`. +- production entry point `.svg-sprite/index.js` with `.d.ts`; +- debug entry point `.svg-sprite/svg-sprite.manifest.js` with `.d.ts`. The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. The names `FileManagerIcon` and `fileManagerIconNames` used below are examples of the API for one possible sprite, not fixed package exports. diff --git a/skills/svg-sprites/src/en/core/10-mode-selection.md b/skills/svg-sprites/src/en/core/10-mode-selection.md index a23a043..509a423 100644 --- a/skills/svg-sprites/src/en/core/10-mode-selection.md +++ b/skills/svg-sprites/src/en/core/10-mode-selection.md @@ -10,24 +10,18 @@ Select exactly one supported mode key: | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | | Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Existing shared config | `legacy` | -Do not use the incomplete `react`, `next@app`, or `next@pages` keys, the future `standalone` mode, or a mode for a different bundler. Install the package as a development dependency and add the local CLI to a package script. The CLI always requires a mode and exactly one path to a configuration directory: +Mode may come from the config, CLI, or programmatic API. Values are applied as `defaults → config → CLI/API overrides`. A mode must exist after merging. + +The CLI accepts exactly one path. A `.ts`, `.js`, or `.json` file loads that exact config regardless of its name. A directory enables config-less generation with settings supplied through CLI flags. ```json { "scripts": { - "sprite:": "svg-sprites --mode " + "sprite:": "svg-sprites ", + "sprite::cli": "svg-sprites --mode " } } ``` -Do not pass multiple paths, a glob, or the path to the config file itself. For multiple modern sprites, create a separate command for each directory. - -Identify legacy mode by its contract, not by the number of sprites: - -- `svg-sprites.config.ts` in the supplied root, with top-level `output` and a non-empty `sprites` array, is a legacy config even if it contains only one entry; -- each legacy entry uses `name`, `input`, and optional `format: 'stack' | 'symbol'`; the `mode` field is deprecated; -- a local `svg-sprite.config.ts` in a single sprite directory, with `name`, `description`, `inputFolder`, `inputFiles`, `transform`, and `generatedNotice`, belongs to the React/Next API even when the application has many such sprites. - -Do not migrate a legacy config without an explicit request. Do not combine `defineLegacyConfig` or the `output`/`sprites` fields with `defineReactSpriteConfig` or `defineNextSpriteConfig`. +Do not use incomplete `react`, `next@app`, or `next@pages` keys, the removed `legacy` mode, or the not-yet-implemented `standalone` mode. Create one command per config file or directory when the project has multiple sprites. diff --git a/skills/svg-sprites/src/en/core/20-project-inspection.md b/skills/svg-sprites/src/en/core/20-project-inspection.md index b9c7cad..8ae0c1a 100644 --- a/skills/svg-sprites/src/en/core/20-project-inspection.md +++ b/skills/svg-sprites/src/en/core/20-project-inspection.md @@ -3,13 +3,13 @@ Establish the project's actual contract before making changes: 1. Read the root `package.json`, lockfile, and workspace configuration; identify the framework, bundler, and existing commands. -2. Find `svg-sprite.config.ts`, `svg-sprites.config.ts`, commands containing `svg-sprites`, and imports of generated components. +2. Find config files, commands containing `svg-sprites`, and imports of generated components. Config names are arbitrary; use the explicit CLI path and object fields. 3. For React, determine whether the project uses Vite or Webpack 5 from its scripts and configuration. For Next.js, separately determine the App/Pages Router and the bundler used by the actual `dev`/`build` commands. 4. Check existing `predev`, `prebuild`, `pretypecheck`, and orchestration scripts. Do not overwrite them. 5. For a new sprite, choose a target directory without imposing a particular application layer or architecture. 6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`. -All paths in a modern config are relative to the directory containing `svg-sprite.config.ts`: +All config paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory: - `inputFolder` defaults to `./icons`; - `inputFiles` contains additional relative paths to individual SVGs and is merged with `inputFolder`; diff --git a/skills/svg-sprites/src/en/core/30-react-next-setup.md b/skills/svg-sprites/src/en/core/30-react-next-setup.md index 3861aaa..57d9ecd 100644 --- a/skills/svg-sprites/src/en/core/30-react-next-setup.md +++ b/skills/svg-sprites/src/en/core/30-react-next-setup.md @@ -21,9 +21,10 @@ npm install --save-dev @gromlab/svg-sprites Use the configuration helper for autocomplete and type checking: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', @@ -31,7 +32,7 @@ export default defineReactSpriteConfig({ }) ``` -The object contract is the same for React and Next.js. Use `defineNextSpriteConfig(...)` instead for a Next.js sprite. +The object contract is the same for React and Next.js; only the full `mode` differs. `name` must begin with an ASCII letter and use kebab-case. The example `file-manager` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`. Another sprite gets its own names. If `name` is omitted, the generator derives it from the directory. @@ -40,15 +41,15 @@ Add a separate command with the selected mode key and one path: ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "sprites": "npm run sprite:file-manager" } } ``` -For Next.js, substitute the full key, for example `next@app/turbopack`. For multiple sprites, add one `sprite:` command per directory and invoke them sequentially from `sprites`. +For Next.js, set the full key in the config, for example `next@app/turbopack`. For multiple sprites, add one `sprite:` command per config file and invoke them sequentially from `sprites`. -Generated files are excluded from Git by default, so run `sprites` before any process that needs `index.ts`, the types, or the asset. Add it to `predev`, `prebuild`, and, when a `typecheck` script exists, `pretypecheck`. +Generated files in `.svg-sprite` are excluded from Git by default, so run `sprites` before any process that needs the component, types, or asset. If the project imports the sprite-module root, create a user-owned `index.ts` with `export * from './.svg-sprite'`. Add generation to `predev`, `prebuild`, and, when a `typecheck` script exists, `pretypecheck`. If a lifecycle script is missing, create it. If it already exists, preserve its command and append generation with `&&`; for example, change `"prebuild": "npm run lint"` to `"prebuild": "npm run lint && npm run sprites"`. Never replace an existing `pre*` script with generation alone, and never create a duplicate JSON key. diff --git a/skills/svg-sprites/src/en/core/40-generated-contract.md b/skills/svg-sprites/src/en/core/40-generated-contract.md index 89c2165..e46359d 100644 --- a/skills/svg-sprites/src/en/core/40-generated-contract.md +++ b/skills/svg-sprites/src/en/core/40-generated-contract.md @@ -5,23 +5,33 @@ After generation, the selected directory has this structure: ```text svg-sprite/ ├── icons/ # user-owned sources -├── svg-sprite.config.ts # user-owned config +├── svg-sprite.config.ts # recommended config name +├── index.ts # optional user-owned barrel ├── .gitignore # managed by the generator -├── index.ts # public production entry point -├── manifest.ts # separate debug entry point -└── generated/ - ├── .svg-sprites.manifest.json # ownership registry - ├── react-component.tsx +└── .svg-sprite/ + ├── state.json # ownership registry and contract version + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts ├── sprite.svg - ├── styles.module.css - └── types.ts + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css ``` -Edit only the source SVGs and `svg-sprite.config.ts`. Do not manually change `.gitignore`, `index.ts`, `manifest.ts`, or anything in `generated/`: the next generation will overwrite them. Import the production API from the root `index.ts`, not through a deep import from `generated/`. +Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change `.gitignore` or anything in `.svg-sprite`: the next generation will overwrite them. To import from the sprite-module root, create a barrel: -The generator owns only the listed root files and immediate files in `generated/`. The `.svg-sprites.manifest.json` registry allows stale generated files to be removed, but the writer refuses to overwrite or delete any file without a generated marker. Do not remove the marker, bypass the refusal, or replace generated paths with symlinks: move the user-owned file or choose a different directory. +```ts +export * from './.svg-sprite' +``` -The public entry point exports the component, props/style types, a readonly array of names, and the icon-name union type. `manifest.ts` contains the URL, target, icon list, and icon metadata for debug tools and is not imported by the production component. +The generator owns `.gitignore` and files inside `.svg-sprite`. The `state.json` registry allows stale generated files to be removed, but the writer refuses to overwrite or delete any file without a generated marker. Do not remove the marker, bypass the refusal, or replace generated paths with symlinks: move the user-owned file or choose a different directory. + +The internal `index.js` exports the component from `react/react-component.js` and the readonly name array; the adjacent `index.d.ts` adds props/style types and the icon-name union. The manifest contains the mode, URL, target, icon list, and icon metadata for debug tools and is not imported by the production component. The sprite remains a separate content-hashed asset; SVG path data is not embedded in JavaScript: diff --git a/skills/svg-sprites/src/en/core/50-usage-and-colors.md b/skills/svg-sprites/src/en/core/50-usage-and-colors.md index e02c4ba..0a75c00 100644 --- a/skills/svg-sprites/src/en/core/50-usage-and-colors.md +++ b/skills/svg-sprites/src/en/core/50-usage-and-colors.md @@ -66,7 +66,7 @@ Automatic replacement targets `fill`/`stroke` attributes and inline `style`. The `SpriteViewer` is optional. Import it from `@gromlab/svg-sprites/react` only on a debug route: -- in Vite, pass the result of a string-literal `import.meta.glob('/src/**/svg-sprite/manifest.ts')`; +- in Vite, pass the result of a string-literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`; - in Webpack, pass an array of static `() => import('.../manifest')` loaders; - in Next.js, use the same static loaders, and for the App Router put the Viewer in a separate file with `'use client'`. diff --git a/skills/svg-sprites/src/en/core/60-verification.md b/skills/svg-sprites/src/en/core/60-verification.md index 1787c7a..a2251a2 100644 --- a/skills/svg-sprites/src/en/core/60-verification.md +++ b/skills/svg-sprites/src/en/core/60-verification.md @@ -2,11 +2,11 @@ After changing a config or SVG, perform these required quick checks: -1. Run the exact sprite command, for example `npm run sprite:file-manager`; it must exit with code `0` and report the name, icon count, mode, and `generated` directory. -2. Confirm that `index.ts`, `manifest.ts`, `generated/sprite.svg`, `generated/react-component.tsx`, `generated/types.ts`, `generated/styles.module.css`, and `generated/.svg-sprites.manifest.json` exist. +1. Run the exact sprite command, for example `npm run sprite:file-manager`; it must exit with code `0` and report the name, icon count, mode, and `.svg-sprite` directory. +2. Confirm that `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, the `icon-data.js`/`.d.ts` and manifest `.js`/`.d.ts` pairs, `react/react-component.js`, its `.d.ts` and CSS Module, and `state.json` exist. 3. Confirm that the new icon appears in the readonly name array and is accepted by the `icon` prop. 4. Run the project's existing type check, for example `npm run typecheck`. -5. Check that `target` in `manifest.ts` matches the selected mode key; the generated asset expression must use `?no-inline` for Vite and `new URL(...)` for Webpack/Next. +5. Check that `target` in `.svg-sprite/svg-sprite.manifest.js` matches the selected mode key; the generated asset expression must use `?no-inline` for Vite and `new URL(...)` for Webpack/Next. Do not run a full production build solely to verify a changed icon list. It is required when the bundler target, asset pipeline configuration, Next router/bundler, or Webpack loader changed, or when diagnosing a runtime URL failure. diff --git a/skills/svg-sprites/src/en/core/70-diagnostics.md b/skills/svg-sprites/src/en/core/70-diagnostics.md index 56f5877..e1a1dff 100644 --- a/skills/svg-sprites/src/en/core/70-diagnostics.md +++ b/skills/svg-sprites/src/en/core/70-diagnostics.md @@ -4,21 +4,20 @@ Match the symptom to the relevant check and fix the root cause: | Symptom | Likely cause | Action | |---|---|---| -| `Missing required argument: --mode` or `Missing sprite path` | Incomplete CLI command | Pass `--mode ` and one sprite directory. | -| `Expected one sprite path` | Multiple paths were passed | Create one command per modern sprite and combine the scripts. | -| `React mode requires a target` or `Unsupported Next.js target` | A shortened or unsupported key was used | Select one of the seven mode keys from the table; do not use `standalone`. | -| `React config file not found` | The CLI path does not point to a directory containing `svg-sprite.config.ts` | Fix the positional path; do not pass the config file itself. | -| Legacy config is missing or read by the wrong pipeline | `svg-sprites.config.ts` and `svg-sprite.config.ts` were confused | Identify the API by the config filename and fields, regardless of the number of `sprites` entries. | +| `Missing sprite config file or module directory` | The positional path is missing | Pass one config file or a directory for config-less generation. | +| `Expected one config file or module directory` | Multiple paths were passed | Create one command per sprite and combine the scripts. | +| `Sprite mode is required` | Mode is absent from both config and CLI | Add `mode` to the object or pass the full `--mode`. | +| `Unsupported sprite config extension` | The supplied file is not `.ts`, `.js`, or `.json` | Use a supported config format. | | `Input directory does not exist` | The relative path is wrong or an explicitly configured folder is missing | Resolve it from the config directory; create the folder or correct `inputFolder`. | | Icons from a subdirectory are missing | Recursive scanning was assumed | Move the SVG to the top level of `inputFolder` or list it in `inputFiles`. | | `SVG file does not exist`, `File is not an SVG`, or an empty input set | Invalid `inputFiles`, extension, or sources | Correct the path/extension and provide at least one input SVG. | | Icon name or SVG ID collision | Two different files have the same basename, or a hash ID collides with a name | Rename one source SVG; do not select a file implicitly. | | `Refusing to overwrite/delete a user file` | A user file occupies a managed path or lost its marker | Do not bypass the protection: move the file or choose another sprite directory and regenerate. | -| Missing `index.ts` or name absent from autocomplete | Generation did not run after the change, or the type server cached an old module | Run the sprite command, then typecheck; restart the TypeScript server if necessary. | +| Missing `.svg-sprite/index.js` or name absent from autocomplete | Generation did not run, the user barrel does not export `.svg-sprite`, or the type server cached an old module | Run the sprite command, check `export * from './.svg-sprite'`, then typecheck; restart the TypeScript server if necessary. | | SVG does not load or the URL is wrong | Mode and bundler differ, Webpack `publicPath` is wrong, or a custom loader intercepted the asset | Align mode with the build command, check Asset Modules/`publicPath`, and exclude the generated SVG from the incompatible loader. | | Next build differs between SSR and browser | The module targets another bundler/router, or the URL was rewritten manually | Restore the generated `new URL(...)`, select the exact Next mode, and regenerate. | | `color` does not change a multicolor icon | The icon uses several variables or is rendered through ``/CSS background | Use ``/`` and the required `--icon-color-N` properties. | | Gradient/filter renders incorrectly | Automatic color replacement cannot guarantee complex paint servers | Inspect the generated SVG; disable `replaceColors` for the sprite or simplify the source if necessary. | | Viewer is empty | Manifests were not generated, the glob/import is not static, or the Client Component boundary is wrong | Generate sprites first; use a literal glob for Vite, static loaders for Webpack/Next, and add `'use client'` only to the App Router Viewer page. | -For an unknown error, record the complete CLI command, mode, config path, and first stack/error message. Then reduce it to one sprite without deleting user files or protective markers. +For an unknown error, record the complete CLI command, mode, config-file or directory path, and first stack/error message. Then reduce it to one sprite without deleting user files or protective markers. diff --git a/skills/svg-sprites/src/en/references/complex-svg.md b/skills/svg-sprites/src/en/references/complex-svg.md index a56b1d9..0d6cfe7 100644 --- a/skills/svg-sprites/src/en/references/complex-svg.md +++ b/skills/svg-sprites/src/en/references/complex-svg.md @@ -12,7 +12,7 @@ Inspect the source SVG before editing it: npm run sprite:file-manager ``` -Use the actual package script for the sprite. Then compare the source with `generated/sprite.svg` and the manifest; do not draw conclusions from a successful exit code alone. +Use the actual package script for the sprite. Then compare the source with `.svg-sprite/sprite.svg` and the manifest; do not draw conclusions from a successful exit code alone. Pay particular attention to: @@ -36,9 +36,10 @@ The compiler first applies SVGO `preset-default` while preserving `viewBox`, the All three options default to `true` and apply to the entire sprite, not to individual icons. ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'illustrations', transform: { removeSize: false, @@ -48,7 +49,7 @@ export default defineReactSpriteConfig({ }) ``` -This is a config for one of potentially many sprite directories in a project; its directory does not have to match a module/feature directory. Use `defineNextSpriteConfig(...)` with the same `transform` for Next; in legacy mode it belongs at the top level of `defineLegacyConfig(...)`. +This is a config for one of potentially many sprite modules in a project; its directory does not have to match a module/feature directory. For Next, use the corresponding full `mode` with the same `transform`. ## Dimensions and viewBox @@ -64,7 +65,7 @@ Correct source preparation: If physical dimensions are part of an illustration's contract, set `removeSize: false` and verify component-prop behavior. Do not preserve width/height as a substitute for a missing viewBox. -React and legacy compilation leave the root sprite `rootViewBox` disabled; Next enables it. Every shape must still have its own valid viewBox, which is included in the manifest and used by the Viewer. +React compilation leaves the root sprite `rootViewBox` disabled; Next enables it. Every shape must still have its own valid viewBox, which is included in the manifest and used by the Viewer. ## Colors @@ -128,7 +129,7 @@ It is preserved as the fragment ID. Other names, such as `folder open.svg` or `2 ``` -Do not manually construct `#folder open`. Use the generated component or `manifest.ts`, which records both `name` and the actual `id`. +Do not manually construct `#folder open`. Use the generated component or `.svg-sprite/svg-sprite.manifest.js`, which records both `name` and the actual `id`. Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; `inputFiles` order does not select a winner. @@ -157,7 +158,7 @@ External stack-fragment support and paint-server behavior can vary across browse 3. Open the generated sprite and find the shape using the ID from the manifest. 4. Statically compare `viewBox`, IDs, `url(#...)`, colors, and inline styles. 5. If the target/pipeline changed or a runtime issue is being diagnosed, build the production bundle and inspect the external hashed SVG. -6. When SpriteViewer, legacy `preview.html`, and visual tools are available, test default colors and each `--icon-color-N` separately. +6. When SpriteViewer and visual tools are available, test default colors and each `--icon-color-N` separately. 7. When browser tools are available and the runtime risk warrants it, test SSR/hydration for Next.js and target browsers for external fragments. 8. Do not claim visual or accessibility equivalence between source and output without the necessary tools and an actual comparison. @@ -172,4 +173,4 @@ External stack-fragment support and paint-server behavior can vary across browse - A manual fragment fails for a name containing spaces: use the ID from the manifest. - One complex icon requires different transforms: move it to a separate sprite; per-icon transform config is not supported. -Target-specific execution and verification are documented in [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), [next-pages.md](next-pages.md), and [legacy.md](legacy.md). +Target-specific execution and verification are documented in [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), and [next-pages.md](next-pages.md). diff --git a/skills/svg-sprites/src/en/references/legacy.md b/skills/svg-sprites/src/en/references/legacy.md deleted file mode 100644 index 2bdb790..0000000 --- a/skills/svg-sprites/src/en/references/legacy.md +++ /dev/null @@ -1,132 +0,0 @@ -# Legacy mode: operational reference - -## When to use this reference - -Use this document when the project already has a root `svg-sprites.config.ts`, centralized output, multiple `sprites` entries, the `symbol` format, or a standalone `preview.html`. Do not migrate such a project to local React/Next modules without an explicit request; for a planned migration, open [migration-1.md](migration-1.md). - -## What distinguishes legacy mode - -- One config manages one or more sprites. -- Results are written as `.sprite.svg` into a shared `output`. -- Both `stack` and `symbol` are supported; the default format is `stack`. -- `preview` is enabled by default and creates a shared `preview.html`. -- There is no generated React component, manifest module, local `.gitignore`, or managed-writer protection. -- Legacy paths in a loaded config are resolved relative to the directory passed to the CLI. - -## Config and execution - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -`svg-sprites.config.ts`: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: [ - 'src/assets/logos/product.svg', - 'src/assets/shared/brand.svg', - ], - format: 'stack', - }, - ], -}) -``` - -Unlike local React/Next configs, each of which describes one of potentially many sprites, a single legacy config retains the specific ability to manage one or more sprite entries. - -Script: - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -Run `npm run sprites` from the project root. - -The CLI path points to the directory containing `svg-sprites.config.ts`. Do not pass the file itself. For a config in another directory, pass that directory explicitly. - -## Input semantics - -- A string `input` denotes a directory; only top-level SVG files are read, sorted by name. -- An array `input` denotes an exact file list; an empty array is an error. -- The extension check is case-sensitive and requires the `.svg` suffix. -- Every file in an array is checked for existence. -- Unlike a local React config, a legacy entry cannot merge a folder and a list. For a mixed set, list every file or split the inputs into separate entries. -- `name` is used directly in the output filename; choose unique safe names and do not create two entries with the same output name. - -## Format selection and runtime - -Use `symbol` for existing `` integration: - -```html - - - -``` - -`stack` works with ``, ``, and CSS URLs. For new React/Next code, do not automatically build a local component over legacy output; select the appropriate modern mode first. - -Page CSS custom properties control content through ``, but do not cross into a document loaded as `` or `background-image`. `symbol` is not intended for ``. - -Names containing spaces or unsafe characters receive a hash ID in the form `icon-<16 hex>`. Do not assume the basename always equals the fragment; inspect the generated SVG or preview. - -## Preview - -With `preview: true`, `output/preview.html` is created after all SVGs. It contains data and inline copies of generated sprites. When diagnosing runtime behavior and browser tools are available, open it to inspect the icon count, `viewBox`, and `--icon-color-N` values. - -With `preview: false`, no new preview is created. The generator is not a cleaner for arbitrary output files: if an old `preview.html` is no longer needed, delete it only after confirming that it is a previous generated artifact. - -## Verification - -```bash -npm run sprites -npm run typecheck -``` - -Generation and the project's typecheck are the required quick checks. For each entry, verify that: - -- `public/sprites/.sprite.svg` exists; -- the logged format and icon count are correct; -- the expected `` or nested `` elements are present; -- `preview.html` is created only when preview is enabled; -- SVGs with complex `defs` were checked according to [complex-svg.md](complex-svg.md). - -Run a production build and browser/Network checks additionally only when the output/public pipeline changed or a runtime issue is being diagnosed. In that case, inspect the real application URL with its public base path and, if visual tools are available, the contents of `preview.html`; do not claim visual or accessibility results without that verification. - -## Common failures - -- `Config file not found`: the CLI path is not a directory containing `svg-sprites.config.ts`. -- `Config file must have a default export`: export the config through `defineLegacyConfig(...)`. -- Deprecated `mode`: the `sprites[].mode` field was renamed to `format`. -- `sprites must be a non-empty array`: a legacy config cannot be empty. -- `Input directory does not exist` or `SVG file does not exist`: remember that paths are resolved from the config directory when loaded by the CLI. -- Nested SVGs are missing: directory scanning is not recursive. -- `Preview template not found` when running from package sources: build the package preview template; it is included in the published distribution. -- Color does not change in ``: it is an isolated SVG document; use `` or colors defined in advance. -- Old output files remain after an entry is removed: the legacy generator writes current results but does not own cleanup of the entire output directory. - -Programmatic execution variants and path-resolution differences are documented in [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/en/references/migration-1.md b/skills/svg-sprites/src/en/references/migration-1.md deleted file mode 100644 index 66544d3..0000000 --- a/skills/svg-sprites/src/en/references/migration-1.md +++ /dev/null @@ -1,185 +0,0 @@ -# Migrating from the legacy 0.1.x API to latest: operational reference - -## When to use this reference - -Use this document only when version `0.1.x` is installed or was previously used, or when the project has old `svg-sprites` invocations without `--mode`, `defineConfig`, `generate`, `loadConfig`, a `sprites[].mode` field, `publicPath`, or a shared `` component. Do not perform this migration as an incidental refactor. - -## Inventory before changes - -First record the project's current contract: - -```bash -npm ls @gromlab/svg-sprites -``` - -Install the current package as a development dependency before updating the config and scripts: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Find and read: - -- `svg-sprites.config.ts` and `svg-sprite.config.ts`; -- scripts that invoke `svg-sprites`; -- imports of `defineConfig`, `generate`, `loadConfig`, and `SvgSprite`; -- references to `publicPath`, `.sprite.svg`, the old generated component, and preview; -- `.gitignore` rules, SVG loaders, and CI generation steps. - -Before changing anything, choose one of the two paths below. Do not combine them in one CLI invocation. - -## Path A: retain the centralized legacy pipeline - -Choose this path when the application depends on shared `public/sprites`, `symbol`, static URLs, or `preview.html`. - -API mapping: - -| Legacy 0.1.x API | Latest API | -|---|---| -| `defineConfig` | `defineLegacyConfig` | -| `sprites[].mode` | `sprites[].format` | -| `generate` | `generateLegacy` | -| `loadConfig` | `loadLegacyConfig` | -| CLI without mode | `svg-sprites --mode legacy ` in a package script | - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy ." - } -} -``` - -Run it with `npm run sprites`. - -`publicPath` and generation of the old shared React component were removed. If the application used that component, legacy mode preserves the SVG asset, but the React wrapper must be replaced separately. The complete legacy workflow is in [legacy.md](legacy.md). - -## Path B: move to local React/Next modules - -Choose this path when typed components, bundler-hashed assets, Server Components, or splitting a large set are required. - -For each required sprite, create a project directory with local `svg-sprite.config.ts` and, when using a folder, `icons/`. The directory does not have to be a module/feature directory; each config describes one of potentially many sprites: - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'global', - inputFolder: './icons', - inputFiles: ['../../../shared/icons/check.svg'], -}) -``` - -For Next.js, use `defineNextSpriteConfig(...)` instead. Then select exactly one mode: - -| Environment | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next App + Turbopack | `next@app/turbopack` | -| Next App + Webpack 5 | `next@app/webpack` | -| Next Pages + Turbopack | `next@pages/turbopack` | -| Next Pages + Webpack 5 | `next@pages/webpack` | - -Example: - -```json -{ - "scripts": { - "sprite:global": "svg-sprites --mode react@vite src/ui/global/svg-sprite", - "sprites": "npm run sprite:global" - } -} -``` - -Run it with `npm run sprite:global`. - -Replace the old generic component: - -```tsx -// Before - - -// After, with name: 'global' - -``` - -The new module creates its own local `.gitignore`, `index.ts`, `manifest.ts`, and `generated/`. The bundler publishes the SVG; do not preserve the old `publicPath` or copy the sprite into `public`. - -Select the detailed procedure: [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), or [next-pages.md](next-pages.md). - -## Programmatic API changes - -The package is now ESM-only: - -```ts -import { compileSpriteContent, generateLegacy } from '@gromlab/svg-sprites' -``` - -Do not use `require()`. `compileSpriteContent(...)` now returns `Promise` rather than a Node-specific `Buffer` declaration: - -```ts -const bytes = await compileSpriteContent(folder) -await fs.promises.writeFile('sprite.svg', bytes) -``` - -If old code called `generate`, replace it with `generateLegacy`; for local modules, use `generateReactSprite` or `generateNextSprite`. See [programmatic-api.md](programmatic-api.md) for exact signatures. - -## Safe migration order - -1. Record the current icon names, formats, public URLs, and color expectations. -2. When migrating imports, update the dependency to latest and update the config API, but do not delete old artifacts until new generation succeeds. -3. Add an explicit mode and directory to every local/CI script. -4. Generate the new output. -5. Replace imports and JSX usages; check the TypeScript icon-name unions. -6. If the target/pipeline changed or a runtime issue is being diagnosed, inspect asset URLs and SSR/SSG; assess visual results only with tools capable of doing so. -7. Only then remove confirmed old generated files and obsolete ignore/loader rules. - -Do not delete an entire directory that may contain source SVGs or user-owned files. The current writer refuses to overwrite files without a generated marker; do not bypass this protection. - -## Post-migration verification - -Minimum checks for an npm project: - -```bash -npm run sprites -npm run typecheck -``` - -Use the project's actual script names. Generation and typechecking are the required quick checks. Add a production build, browser, and Network checks only when the migration changes the target/pipeline or a runtime issue is being diagnosed. Verify that: - -- no script invokes the CLI without `--mode` and a path; -- the config helper and CLI mode belong to the same pipeline; -- a local manifest contains the expected target, while legacy output uses the expected format; -- obsolete `publicPath`, `sprites[].mode`, and removed API imports are gone; -- generated files are created before typechecking/building in a clean checkout; -- every old icon name remains available or was intentionally renamed; -- colors and complex SVGs were examined according to [complex-svg.md](complex-svg.md), without claims of visual or accessibility correctness when tools are unavailable; -- `SpriteViewer` replaced a separate preview only for React/Next; legacy mode may still use `preview.html`. - -## Common failures - -- `Missing required argument: --mode`: an old script was not updated. -- `React mode requires a target` or `Next.js mode requires a router and bundler`: a shortened mode was used instead of the full key. -- Deprecated `mode`: a legacy entry still has `sprites[].mode`. -- `icons was renamed to inputFolder`: the old field was moved into a local React/Next config without being renamed. -- `require() of ES Module`: the build script was not converted to ESM/import. -- Icons disappeared: local `inputFolder` scanning is shallow, or shared files were not added through `inputFiles`. -- Runtime still requests old `/sprites/...` URLs: JSX/CSS usage was not migrated to the generated component or new asset URL. -- One sprite is generated with multiple targets: CI and local scripts disagree; retain one target matching the actual bundler. diff --git a/skills/svg-sprites/src/en/references/next-app.md b/skills/svg-sprites/src/en/references/next-app.md index 1895279..44e8413 100644 --- a/skills/svg-sprites/src/en/references/next-app.md +++ b/skills/svg-sprites/src/en/references/next-app.md @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', @@ -41,7 +42,7 @@ export default defineNextSpriteConfig({ ``` - The project chooses the directory for each specific sprite; it does not have to be a module/feature directory. Each config describes one of potentially many application sprites. -- The config filename is singular: `svg-sprite.config.ts`. +- `svg-sprite.config.ts` is the recommended name; the CLI accepts an explicitly selected `.ts`, `.js`, or `.json` file with any name. - Paths are relative to its directory; `inputFolder` scanning is shallow. - The local folder and `inputFiles` are merged. Equal basenames from different files are forbidden. - If no name is configured, it is derived from the directory; for a directory named `svg-sprite`, the parent directory name is used. An explicit `name` is more stable when files move. @@ -56,7 +57,7 @@ Example scripts for Turbopack: ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -84,7 +85,7 @@ export default function Page() { `width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. Do not add a Client Component boundary solely for the icon. The generated module builds the asset URL with static `new URL('./sprite.svg', import.meta.url).href`; the same mechanism is used during SSR and in the browser. -Do not import from `generated/` or move the SVG into `public`. Managed files (`generated/`, `index.ts`, `manifest.ts`, `.gitignore`) are regenerated. +Do not move the SVG into `public`. The generator rewrites `.svg-sprite` and `.gitignore`; the root `index.ts` is application-owned and may contain `export * from './.svg-sprite'`. ## SpriteViewer @@ -124,7 +125,7 @@ npm run build After the required generation and typecheck, verify that: -- `manifest.ts` contains the exact target `next@app/turbopack` or `next@app/webpack`; +- `.svg-sprite/svg-sprite.manifest.js` contains the exact target `next@app/turbopack` or `next@app/webpack`; - the server page compiles without adding `'use client'`. During a conditional production/runtime check, also verify that: @@ -140,7 +141,7 @@ During a conditional production/runtime check, also verify that: - Build passes with one bundler but the runtime asset breaks with another: regenerate for the target actually used by the build. - Webpack was selected, but the build used Turbopack: use the project's Webpack build command and `next@app/webpack`. - Viewer causes a Server Component error: the Viewer file needs `'use client'`; the generated icon component does not. -- `React config file not found`: the command received the path to `app/`, `icons/`, or the config file instead of the sprite directory. +- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file. - Two CI jobs generate different targets in one checkout: separate their directories or ensure each job uses one consistent target. - Asset is missing under `basePath`/CDN: inspect Next asset handling and deployment configuration; do not replace the generated URL manually. - Package subpath type error: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. diff --git a/skills/svg-sprites/src/en/references/next-pages.md b/skills/svg-sprites/src/en/references/next-pages.md index 6081376..a24d531 100644 --- a/skills/svg-sprites/src/en/references/next-pages.md +++ b/skills/svg-sprites/src/en/references/next-pages.md @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@pages/webpack', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', @@ -53,7 +54,7 @@ Example lifecycle scripts for Webpack: ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -79,7 +80,7 @@ export function getServerSideProps() { `width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The component works during SSR, SSG, and client navigation. It uses `new URL('./sprite.svg', import.meta.url).href` so Next emits an external hashed asset. Do not copy the generated SVG into `public` or construct the URL manually. -Import the component and types only from the local `svg-sprite/index.ts`. Do not edit `generated/`, `index.ts`, `manifest.ts`, or the generated `.gitignore`. +Import the component and types from the local user-owned `svg-sprite/index.ts`, which re-exports `.svg-sprite`. Do not edit `.svg-sprite` or the generated `.gitignore`. ## SpriteViewer @@ -115,7 +116,7 @@ npm run build After the required generation and typecheck, verify that: -- `manifest.ts` contains `next@pages/turbopack` or `next@pages/webpack`; +- `.svg-sprite/svg-sprite.manifest.js` contains `next@pages/turbopack` or `next@pages/webpack`; - `getServerSideProps`/`getStaticProps` does not import the package React Viewer entry. After the conditional production build, and only when browser tools are available, inspect the SSR route and navigation to it: diff --git a/skills/svg-sprites/src/en/references/programmatic-api.md b/skills/svg-sprites/src/en/references/programmatic-api.md index c597a30..42727c0 100644 --- a/skills/svg-sprites/src/en/references/programmatic-api.md +++ b/skills/svg-sprites/src/en/references/programmatic-api.md @@ -1,244 +1,41 @@ # Programmatic API: operational reference -## Required installation +Use `generateSprite(source, overrides?)` as the primary Node.js API. -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Programmatic imports require the local development dependency shown above. This reference describes the installed package API. - -## When to use this reference - -Use this document when generation runs from an ESM build script, monorepo orchestration, custom CLI, or test rather than directly from a package script. For ordinary integration, prefer the CLI references: [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), [next-pages.md](next-pages.md), or [legacy.md](legacy.md). - -## Runtime and TypeScript - -The main entry point is ESM-only and does not import React: +## From a config file ```ts -import { generateReactSprite } from '@gromlab/svg-sprites' +import { generateSprite } from '@gromlab/svg-sprites' + +await generateSprite('src/ui/icons/svg-sprite.config.ts') ``` -Node.js 18+ is required. Do not use `require()`. The `@gromlab/svg-sprites/react` package subpath requires TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. +`source` must point to a specific `.ts`, `.js`, or `.json` file. The file name is arbitrary and no discovery is performed. Its directory becomes the sprite module root and the base for relative paths. -## React module +## Without a config file ```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -Target signature: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -`root` is resolved relative to the current `process.cwd()`. It is the selected directory for the specific sprite, not a required module/feature directory. Within it, the function loads only `svg-sprite.config.ts`, merges `inputFolder` and `inputFiles`, compiles `stack`, and safely updates managed files. Each such config describes one of potentially many sprites. - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -Pass the target explicitly. A value outside the union synchronously throws `Unsupported React asset target`. - -## Next.js module - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -The result contains the React result fields, a `next@/` target, and `router` and `bundler`. Next generation always uses `stack` and includes a root `viewBox`. The selected options must match the actual Next build command. - -## Config helpers and loaders - -Helpers only return the object and provide autocomplete; they do not load files or run validation: - -```ts -import { - defineLegacyConfig, - defineNextSpriteConfig, - defineReactSpriteConfig, - loadLegacyConfig, - loadReactSpriteConfig, -} from '@gromlab/svg-sprites' -``` - -```ts -const reactConfig = await loadReactSpriteConfig( - 'src/ui/file-manager/svg-sprite', -) - -const legacyConfig = await loadLegacyConfig('.') -``` - -`loadReactSpriteConfig(root)` resolves `inputFolder`/`inputFiles` from `root` and returns a normalized config. Despite its name, Next mode uses the same loader. - -`loadLegacyConfig(cwd)` looks for `cwd/svg-sprites.config.ts`, validates it, and converts `output` and `sprites[].input` into absolute paths relative to `cwd`. - -## Legacy generation - -Direct invocation: - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], +await generateSprite('src/ui/icons', { + mode: 'react@vite', + name: 'app', + inputFolder: './icons', }) ``` -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` +A directory enables config-less mode. `mode` must exist after settings are merged. -Important path nuance: `generateLegacy(config)` does not know the config file's location. Relative `output` and `input` paths are resolved from the current `process.cwd()`. If semantics must match the CLI for a config in another directory, call the loader first: +## Overrides ```ts -const config = await loadLegacyConfig('config/sprites') -const results = await generateLegacy(config) -``` - -## Low-level compilation - -Main exports: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -Minimal in-memory example: - -```ts -import { compileSpriteContent, resolveSpriteEntry } from '@gromlab/svg-sprites' - -const folder = resolveSpriteEntry({ - name: 'icons', - input: 'src/assets/icons', - format: 'stack', +await generateSprite('src/ui/icons/custom.json', { + mode: 'react@webpack', + inputFiles: ['../../shared/search.svg'], + transform: { addTransition: false }, }) - -const bytes = await compileSpriteContent( - folder, - { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - { rootViewBox: false }, -) ``` -`compileSpriteContent` returns `Promise` and does not write to disk. `compileSprite(folder, outputDir, transform?, options?)` creates the output directory and writes `.sprite.svg`. +Values are applied as `defaults → config → API overrides`. `transform` is merged by field; a supplied `inputFiles` replaces the config array. -`CompileSpriteOptions.rootViewBox` defaults to `false`; the standard Next preset passes `true`, while React and legacy leave it `false`. Do not change this option in an attempt to replace the target: it does not determine how the asset is published. +The specialized `generateReactSprite` and `generateNextSprite` functions remain as compatibility wrappers, but prefer `generateSprite` in new code. -`resolveSpriteEntry` and `resolveSprites` resolve source paths relative to `process.cwd()`, verify existence, and read only the top level of a directory. They do not implement local `inputFolder + inputFiles` semantics; use the high-level generators for standard React/Next modules. - -`createShapeTransform(options)` returns a transform callback for `svg-sprite`. It is an integration primitive, not a `string -> string` function. - -`generatePreview(results, outputDir)` expects every `results[].spritePath` to exist and writes `preview.html` from the package template. Do not invoke it before compilation. - -## React debug runtime - -The Viewer is exposed through a separate client entry: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { - SpriteManifest, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -`SpriteViewerSources` accepts an array or record of manifests/loaders, including the result of Vite's `import.meta.glob`. A loader may return a manifest directly or a module with `default`/`spriteManifest`. An invalid module fails with `The loaded module does not export a valid SVG sprite manifest.` - -The package React entry contains `'use client'`; do not import it from server-only build scripts or production icon modules. The generated production component lives in the selected sprite directory. - -## Orchestrating multiple modules - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const roots = [ - 'src/ui/global/svg-sprite', - 'src/ui/file-manager/svg-sprite', -] - -const results = await Promise.all( - roots.map((root) => generateReactSprite(root, 'vite')), -) - -if (results.some((result) => result.iconCount === 0)) { - throw new Error('Empty sprite module') -} -``` - -Do not run different targets concurrently for one root: both calls own the same generated paths. - -## Verification and error handling - -Run programmatic generation and the project's typecheck; these are the required quick checks. After invocation, inspect `result.target`, `iconCount`, and the existence of `spritePath`/`manifestPath`. Add a real bundler build and browser/Network checks only when the target/pipeline changed or a runtime issue is being diagnosed; do not claim visual or accessibility results without the necessary tools. - -Common failure causes: - -- an unsupported target/router/bundler was passed as an unchecked string; -- `process.cwd()` differs in a monorepo runner, so a relative root points elsewhere; -- the config has no default export; -- an explicitly configured `inputFolder` is missing; -- two source files produce the same fragment ID; -- custom orchestration attempts to overwrite a user-owned file in a managed path; -- `generatePreview` cannot find its template when running from unbuilt package sources; -- a complex SVG requires individual transforms to be disabled according to [complex-svg.md](complex-svg.md). +For custom orchestration, use `loadSpriteConfig`, `validateSpriteConfig`, `resolveSpriteConfig`, `compileSpriteContent`, and `createShapeTransform`. diff --git a/skills/svg-sprites/src/en/references/react-vite.md b/skills/svg-sprites/src/en/references/react-vite.md index 8e35fc6..f4daf52 100644 --- a/skills/svg-sprites/src/en/references/react-vite.md +++ b/skills/svg-sprites/src/en/references/react-vite.md @@ -8,8 +8,8 @@ Use this document when the project uses React without Next.js and is built with 1. Check `package.json`: confirm React, Vite, and the actual `dev`, `build`, and `typecheck` commands. 2. Find existing `svg-sprite.config.ts` files and scripts containing `svg-sprites`. Do not create a second directory for an existing sprite. -3. Choose a target directory for the specific sprite. It does not have to be a module or feature directory: the generator accepts the directory containing the config, not the config file or `icons/` path. -4. Do not manually edit `generated/`, `index.ts`, `manifest.ts`, or the local `.gitignore`; the generator owns them. +3. Choose a target directory for the specific sprite. It does not have to be a module or feature directory; pass the full path to its config file. +4. Do not manually edit `.svg-sprite` or the local `.gitignore`; the generator owns them. The root `index.ts` is application-owned. Minimal structure: @@ -32,9 +32,10 @@ npm install --save-dev @gromlab/svg-sprites `src/ui/file-manager/svg-sprite/svg-sprite.config.ts`: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', @@ -58,7 +59,7 @@ Add the local CLI to `package.json` and run it before processes that need genera ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -95,7 +96,7 @@ export const availableIcons = fileManagerIconNames ``` -Do not deep-import from `generated/`; its file structure is not an integration point. +For normal imports, use a user-owned barrel with `export * from './.svg-sprite'`; the remaining files in the hidden directory are internal. ## Target-specific behavior @@ -110,14 +111,14 @@ The `?no-inline` query is required: it prevents Vite from converting a small SVG Use the same mechanism for low-level `` integration: ```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' +import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline' ``` -A manual `#check` fragment is safe only for names matching `^[a-zA-Z][a-zA-Z0-9_-]*$`. For spaces and other characters, the generated component uses a stable hash ID; the exact ID is available in `manifest.ts`. +A manual `#check` fragment is safe only for names matching `^[a-zA-Z][a-zA-Z0-9_-]*$`. For spaces and other characters, the generated component uses a stable hash ID; the exact ID is available in `.svg-sprite/svg-sprite.manifest.js`. ## SpriteViewer @@ -128,7 +129,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export function IconsDebugPage() { @@ -136,7 +137,7 @@ export function IconsDebugPage() { } ``` -The `import.meta.glob` argument must remain a string literal. Generation must finish before Vite starts, or new `manifest.ts` files will not be included in the glob. +The `import.meta.glob` argument must remain a string literal. Generation must finish before Vite starts, or new manifests will not be included in the glob. ## Verification @@ -147,18 +148,18 @@ npm run typecheck These are the required quick checks. Inspect the generated files statically: -- public `index.ts` exports the component, props, style, name union, and runtime array; -- `manifest.ts` contains `target: "vite"`, `format: "stack"`, and the expected icon count. +- the user-owned `index.ts` re-exports the component, props, style, name union, and runtime array from `.svg-sprite`; +- `.svg-sprite/svg-sprite.manifest.js` contains `target: "vite"`, `format: "stack"`, and the expected icon count. If the target or asset pipeline changed, or a runtime issue is being diagnosed, also run the production build. When browser tools are available, inspect Network for a separate `.svg` asset rather than `data:image/svg+xml`, a successful URL, and ``. For complex colors, `defs`, and dimensions, follow [complex-svg.md](complex-svg.md); do not claim a visual or accessibility result without the necessary tools. ## Common failures -- `React config file not found`: the command points to `icons/` or the config file; pass the directory containing `svg-sprite.config.ts`. -- `React mode requires a target`: `--mode react` was used; the mode must be exactly `react@vite`. +- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file. +- `Sprite mode is required`: set `mode: 'react@vite'` in the config or pass `--mode react@vite`. - Icon missing from autocomplete: check the case-sensitive `.svg` suffix and shallow location, then regenerate before typechecking. - `Refusing to overwrite a user file`: do not remove the marker or bypass the writer; move the user-owned file or choose another sprite directory. -- Viewer is empty: check the literal glob, the existence of generated `manifest.ts`, and the `predev` execution order. +- Viewer is empty: check the literal glob, the existence of `.svg-sprite/svg-sprite.manifest.js`, and the `predev` execution order. - SVG was inlined: confirm that the module targets `vite` and its import still contains `?no-inline`. - TypeScript cannot resolve the package subpath: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. diff --git a/skills/svg-sprites/src/en/references/react-webpack.md b/skills/svg-sprites/src/en/references/react-webpack.md index b6d7e73..46c71ac 100644 --- a/skills/svg-sprites/src/en/references/react-webpack.md +++ b/skills/svg-sprites/src/en/references/react-webpack.md @@ -9,7 +9,7 @@ Use this document for a React application built with Webpack 5 when SVGs must be 1. Confirm Webpack major version 5 in `package.json` or the lockfile. 2. Inspect `.svg` entries in `module.rules`, `output.publicPath`, the dev server, and existing asset conventions. 3. Find existing `svg-sprite.config.ts` files and generation scripts. -4. Choose the project directory for the specific sprite. It does not have to match a module or feature directory. The command accepts the directory containing the config, not the config file or icons folder. +4. Choose the project directory for the specific sprite. It does not have to match a module or feature directory. Pass the full path to its config file. Minimal structure: @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@webpack', name: 'file-manager', description: 'File manager icons', inputFolder: './icons', @@ -49,7 +50,7 @@ Recommended lifecycle hooks: ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -74,7 +75,7 @@ export function OpenFolderButton() { } ``` -`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The public entry point also exports `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName`, and `fileManagerIconNames`. Do not import files directly from `generated/`, and do not edit `generated/`, `index.ts`, `manifest.ts`, or the generated `.gitignore`. +`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The user-owned barrel also exports `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName`, and `fileManagerIconNames`. Do not edit `.svg-sprite` or the generated `.gitignore`. ## Webpack target behavior @@ -88,7 +89,7 @@ Webpack 5 recognizes it as an Asset Module and replaces it with the public URL o - do not move the path into a variable or modify the generated expression; - ensure Babel/TypeScript does not transform `import.meta.url` before Webpack processes it; -- do not let `@svgr/webpack`, `svg-inline-loader`, `raw-loader`, or a general SVG rule intercept `svg-sprite/generated/sprite.svg`; +- do not let `@svgr/webpack`, `svg-inline-loader`, `raw-loader`, or a general SVG rule intercept `svg-sprite/.svg-sprite/sprite.svg`; - with custom rules, either exclude the generated sprite from component/raw loaders or add a dedicated `type: 'asset/resource'` rule; - check `output.publicPath`, especially for CDN, subpath, and dev-server deployments. @@ -96,7 +97,7 @@ Example dedicated rule when an existing loader intercepts every SVG: ```js { - test: /svg-sprite[\\/]generated[\\/]sprite\.svg$/, + test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/, type: 'asset/resource', } ``` @@ -131,7 +132,7 @@ npm run typecheck These are the required quick checks. After generation, verify that: -- `manifest.ts` contains `target: "webpack"` and `format: "stack"`; +- `.svg-sprite/svg-sprite.manifest.js` contains `target: "webpack"` and `format: "stack"`; - the generated component contains `new URL('./sprite.svg', import.meta.url).href`. A production build and browser/Network checks are additionally required when the target, Webpack asset rules, `publicPath`/deployment pipeline changed, or a runtime issue is being diagnosed. In that case, verify a separate hashed SVG asset, an HTTP(S) `.svg#id` URL, content type, fragment ID, and the correct CDN/subpath URL. Do not claim visual or accessibility correctness without the necessary tools. @@ -145,7 +146,7 @@ For icons with gradients, filters, masks, or internal CSS, perform the checks in - URL points to the wrong host/subpath: fix `output.publicPath` and runtime deployment settings, not the generated file. - `import.meta` is unsupported: confirm the build really uses Webpack 5 and that an intermediate transpiler preserves the expression. - Viewer does not load the manifest: check the literal path, chunk loading, and generation before compilation. -- `Refusing to overwrite a user file`: the directory already contains a user-owned `index.ts`, `manifest.ts`, `.gitignore`, or file in `generated/`; move it rather than bypassing protection. +- `Refusing to overwrite a user file`: the directory already contains a user-owned `.gitignore` or file in `.svg-sprite`; move it rather than bypassing protection. - Icon color does not change: use `color` for monochrome icons or `--icon-color-N` through ``; page CSS does not cross into ``. For custom build orchestration, see [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/SKILL.md b/skills/svg-sprites/src/ru/SKILL.md index 49bf96b..6b0c635 100644 --- a/skills/svg-sprites/src/ru/SKILL.md +++ b/skills/svg-sprites/src/ru/SKILL.md @@ -13,7 +13,6 @@ - Для React + Vite открой [React + Vite](./references/react-vite.md), для React с собственным Webpack 5 — [React + Webpack](./references/react-webpack.md). - Для Next.js открой руководство для [App Router](./references/next-app.md) или [Pages Router](./references/next-pages.md) и выбери раздел фактического сборщика. -- При `svg-sprites.config.ts` открой [legacy mode](./references/legacy.md); при переходе с `0.1.x` дополнительно открой [миграцию](./references/migration-1.md). - Для вызова генератора из Node.js открой [программный API](./references/programmatic-api.md). - Для gradients, filters, `url(#...)`, нестандартных цветов и проблем с `viewBox` открой [сложные SVG](./references/complex-svg.md). - Полную пользовательскую документацию используй как вторичный источник: [README пакета](./references/upstream/README_RU.md). diff --git a/skills/svg-sprites/src/ru/core/00-package-overview.md b/skills/svg-sprites/src/ru/core/00-package-overview.md index 30629ad..dd12b1f 100644 --- a/skills/svg-sprites/src/ru/core/00-package-overview.md +++ b/skills/svg-sprites/src/ru/core/00-package-overview.md @@ -2,13 +2,13 @@ `@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний кешируемый sprite asset и для React/Next.js создаёт типизированный компонент, список допустимых имён и debug manifest. -Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый выбранный каталог с `svg-sprite.config.ts` описывает один спрайт и получает собственные: +Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные: - SVG asset; - типы имён иконок; - React-компонент; -- production entry `index.ts`; -- debug entry `manifest.ts`. +- production entry `.svg-sprite/index.js` с `.d.ts`; +- debug entry `.svg-sprite/svg-sprite.manifest.js` с `.d.ts`. Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Имена `FileManagerIcon` и `fileManagerIconNames` ниже являются примерами API одного из возможных спрайтов, а не фиксированными экспортами пакета. diff --git a/skills/svg-sprites/src/ru/core/10-mode-selection.md b/skills/svg-sprites/src/ru/core/10-mode-selection.md index 638bab8..c7dceeb 100644 --- a/skills/svg-sprites/src/ru/core/10-mode-selection.md +++ b/skills/svg-sprites/src/ru/core/10-mode-selection.md @@ -10,24 +10,18 @@ | Next.js App Router + Webpack 5 | `next@app/webpack` | | Next.js Pages Router + Turbopack | `next@pages/turbopack` | | Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Старый общий конфиг | `legacy` | -Не используй неполные `react`, `next@app`, `next@pages`, будущий `standalone` или mode другого сборщика. Установи пакет как development dependency и добавь локальный CLI в package script. CLI всегда требует mode и ровно один путь к каталогу конфигурации: +Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен. + +CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI. ```json { "scripts": { - "sprite:": "svg-sprites --mode " + "sprite:": "svg-sprites ", + "sprite::cli": "svg-sprites --mode " } } ``` -Не передавай несколько путей, glob или путь к самому файлу конфигурации. Для нескольких современных спрайтов создай отдельную команду для каждого каталога. - -Определяй legacy по контракту, а не по количеству спрайтов: - -- `svg-sprites.config.ts` в переданном корне с верхнеуровневыми `output` и непустым массивом `sprites` является legacy-конфигурацией даже при одном элементе; -- каждый legacy-элемент использует `name`, `input` и необязательный `format: 'stack' | 'symbol'`; поле `mode` устарело; -- локальный `svg-sprite.config.ts` в каталоге одного спрайта с `name`, `description`, `inputFolder`, `inputFiles`, `transform` и `generatedNotice` относится к React/Next API даже если таких спрайтов в приложении много. - -Не мигрируй legacy-конфигурацию без явного запроса. Не смешивай `defineLegacyConfig` и поля `output`/`sprites` с `defineReactSpriteConfig` или `defineNextSpriteConfig`. +Не используй неполные `react`, `next@app`, `next@pages`, удалённый `legacy` или ещё не реализованный `standalone`. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. diff --git a/skills/svg-sprites/src/ru/core/20-project-inspection.md b/skills/svg-sprites/src/ru/core/20-project-inspection.md index 0a20ce3..309b129 100644 --- a/skills/svg-sprites/src/ru/core/20-project-inspection.md +++ b/skills/svg-sprites/src/ru/core/20-project-inspection.md @@ -3,13 +3,13 @@ До изменений установи фактический контракт проекта: 1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды. -2. Найди `svg-sprite.config.ts`, `svg-sprites.config.ts`, команды `svg-sprites` и импорты generated-компонентов. +2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта. 3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд. 4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их. 5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения. 6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`. -Все пути современного конфига считаются относительно каталога, содержащего `svg-sprite.config.ts`: +Все пути конфига считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога: - `inputFolder` по умолчанию равен `./icons`; - `inputFiles` содержит дополнительные относительные пути к отдельным SVG и объединяется с `inputFolder`; diff --git a/skills/svg-sprites/src/ru/core/30-react-next-setup.md b/skills/svg-sprites/src/ru/core/30-react-next-setup.md index 9fa22b8..8214bad 100644 --- a/skills/svg-sprites/src/ru/core/30-react-next-setup.md +++ b/skills/svg-sprites/src/ru/core/30-react-next-setup.md @@ -21,9 +21,10 @@ npm install --save-dev @gromlab/svg-sprites Используй config helper для autocomplete и проверки типов: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', @@ -31,7 +32,7 @@ export default defineReactSpriteConfig({ }) ``` -Контракт объекта одинаков для React и Next.js. Для Next.js-спрайта используй `defineNextSpriteConfig(...)`. +Контракт объекта одинаков для React и Next.js; отличается полный `mode`. `name` должен начинаться с латинской буквы и записываться в kebab-case; из примера `file-manager` будут созданы `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`. Другой спрайт получает собственные имена. Если `name` не задан, генератор выводит его из каталога. @@ -40,15 +41,15 @@ export default defineReactSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "sprites": "npm run sprite:file-manager" } } ``` -Для Next.js подставь полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:` на каждый каталог и последовательно вызови их из `sprites`. +Для Next.js укажи в config полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:` на каждый config-файл и последовательно вызови их из `sprites`. -Generated-файлы по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны `index.ts`, типы или asset. Добавь вызов к `predev`, `prebuild` и, если есть `typecheck`, к `pretypecheck`. +Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Добавь генерацию к `predev`, `prebuild` и, если есть `typecheck`, к `pretypecheck`. Если lifecycle script отсутствует, создай его. Если он уже существует, сохрани его команду и допиши генерацию через `&&`, например преобразуй `"prebuild": "npm run lint"` в `"prebuild": "npm run lint && npm run sprites"`. Никогда не заменяй существующий `pre*` одной генерацией и не создавай второй одноимённый JSON key. diff --git a/skills/svg-sprites/src/ru/core/40-generated-contract.md b/skills/svg-sprites/src/ru/core/40-generated-contract.md index cfd6d7e..fa1cc95 100644 --- a/skills/svg-sprites/src/ru/core/40-generated-contract.md +++ b/skills/svg-sprites/src/ru/core/40-generated-contract.md @@ -5,23 +5,33 @@ ```text svg-sprite/ ├── icons/ # пользовательские исходники -├── svg-sprite.config.ts # пользовательский конфиг +├── svg-sprite.config.ts # рекомендуемое имя конфига +├── index.ts # необязательный пользовательский barrel ├── .gitignore # управляет генератор -├── index.ts # публичная production-точка входа -├── manifest.ts # отдельная debug-точка входа -└── generated/ - ├── .svg-sprites.manifest.json # реестр владения - ├── react-component.tsx +└── .svg-sprite/ + ├── state.json # реестр владения и версия контракта + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts ├── sprite.svg - ├── styles.module.css - └── types.ts + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css ``` -Редактируй только исходные SVG и `svg-sprite.config.ts`. Не изменяй вручную `.gitignore`, `index.ts`, `manifest.ts` и содержимое `generated/`: повторная генерация их перезапишет. Импортируй production API из корневого `index.ts`, а не deep import из `generated/`. +Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную `.gitignore` и содержимое `.svg-sprite`: повторная генерация их перезапишет. Для импорта из корня sprite-модуля создай barrel: -Генератор владеет только перечисленными корневыми файлами и непосредственными файлами `generated/`. Реестр `.svg-sprites.manifest.json` позволяет удалить устаревший generated-файл, но writer откажется перезаписывать или удалять файл без generated-маркера. Не удаляй маркер, не обходи отказ и не подменяй generated-пути symlink: перенеси пользовательский файл или выбери другой каталог. +```ts +export * from './.svg-sprite' +``` -Публичная точка входа экспортирует компонент, props/style-типы, readonly-массив имён и union-тип имени. `manifest.ts` содержит URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом. +Генератор владеет `.gitignore` и файлами внутри `.svg-sprite`. Реестр `state.json` позволяет удалить устаревший generated-файл, но writer откажется перезаписывать или удалять файл без generated-маркера. Не удаляй маркер, не обходи отказ и не подменяй generated-пути symlink: перенеси пользовательский файл или выбери другой каталог. + +Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом. Спрайт остаётся отдельным asset с content hash; SVG path-данные не встраиваются в JavaScript: diff --git a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md b/skills/svg-sprites/src/ru/core/50-usage-and-colors.md index 90ca00c..99f2974 100644 --- a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md +++ b/skills/svg-sprites/src/ru/core/50-usage-and-colors.md @@ -66,7 +66,7 @@ Generated-компонент не выбирает семантику за пр `SpriteViewer` необязателен. Подключай его из `@gromlab/svg-sprites/react` только на debug-маршруте: -- в Vite передай результат строкового literal `import.meta.glob('/src/**/svg-sprite/manifest.ts')`; +- в Vite передай результат строкового literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`; - в Webpack передай массив статических `() => import('.../manifest')`; - в Next.js используй такие же статические loaders, а для App Router помести Viewer в отдельный файл с `'use client'`. diff --git a/skills/svg-sprites/src/ru/core/60-verification.md b/skills/svg-sprites/src/ru/core/60-verification.md index 34b41d9..82d7d65 100644 --- a/skills/svg-sprites/src/ru/core/60-verification.md +++ b/skills/svg-sprites/src/ru/core/60-verification.md @@ -2,11 +2,11 @@ После изменения конфига или SVG выполни обязательные быстрые проверки: -1. Запусти точную sprite-команду, например `npm run sprite:file-manager`; процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `generated`. -2. Проверь наличие `index.ts`, `manifest.ts`, `generated/sprite.svg`, `generated/react-component.tsx`, `generated/types.ts`, `generated/styles.module.css` и `generated/.svg-sprites.manifest.json`. +1. Запусти точную sprite-команду, например `npm run sprite:file-manager`; процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`. +2. Проверь наличие `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, пары `icon-data.js`/`.d.ts`, manifest `.js`/`.d.ts`, `react/react-component.js`, его `.d.ts` и CSS Module, а также `state.json`. 3. Убедись, что новая иконка присутствует в readonly-массиве имён и принимается prop `icon`. 4. Запусти существующую проверку типов проекта, например `npm run typecheck`. -5. Проверь в `manifest.ts`, что `target` совпадает с выбранным mode key; generated asset expression должен быть `?no-inline` для Vite и `new URL(...)` для Webpack/Next. +5. Проверь в `.svg-sprite/svg-sprite.manifest.js`, что `target` совпадает с выбранным mode key; generated asset expression должен быть `?no-inline` для Vite и `new URL(...)` для Webpack/Next. Не запускай полную production-сборку только ради проверки изменения списка иконок. Она нужна, если менялся bundler target, конфигурация asset pipeline, Next router/bundler, Webpack loader или диагностируется ошибка URL в runtime. diff --git a/skills/svg-sprites/src/ru/core/70-diagnostics.md b/skills/svg-sprites/src/ru/core/70-diagnostics.md index cde2648..88350a4 100644 --- a/skills/svg-sprites/src/ru/core/70-diagnostics.md +++ b/skills/svg-sprites/src/ru/core/70-diagnostics.md @@ -4,21 +4,20 @@ | Симптом | Вероятная причина | Действие | |---|---|---| -| `Missing required argument: --mode` или `Missing sprite path` | Неполная CLI-команда | Передай `--mode <полный-key>` и один каталог спрайта. | -| `Expected one sprite path` | Передано несколько путей | Создай отдельную команду на каждый современный спрайт и объедини scripts. | -| `React mode requires a target` или `Unsupported Next.js target` | Использован сокращённый/неподдерживаемый key | Выбери один из семи mode keys из таблицы, не используй `standalone`. | -| `React config file not found` | CLI указывает не на каталог с `svg-sprite.config.ts` | Исправь позиционный путь; не передавай путь к файлу. | -| Legacy config не найден или читается не тем pipeline | Перепутаны `svg-sprites.config.ts` и `svg-sprite.config.ts` | Определи API по имени и полям конфига, независимо от числа элементов `sprites`. | +| `Missing sprite config file or module directory` | Не передан позиционный путь | Передай один config-файл либо каталог для config-less запуска. | +| `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. | +| `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. | +| `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. | | `Input directory does not exist` | Ошибка относительного пути или отсутствует явно заданная папка | Считай путь от каталога конфига; создай папку или исправь `inputFolder`. | | Иконки из подпапки не появились | Ожидался recursive scan | Перемести SVG на верхний уровень `inputFolder` или перечисли его в `inputFiles`. | | `SVG file does not exist`, `File is not an SVG` или пустой набор | Неверный `inputFiles`, расширение или источники | Исправь путь/расширение и обеспечь хотя бы один входной SVG. | | Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. | | `Refusing to overwrite/delete a user file` | Пользовательский файл занял managed-путь или потерял marker | Не обходи защиту: перенеси файл либо выбери другой sprite-каталог и перегенерируй. | -| Нет `index.ts` или имя отсутствует в autocomplete | Генерация не запускалась после изменения либо type server держит старый модуль | Запусти sprite-команду, затем typecheck; при необходимости перезапусти TypeScript server. | +| Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Генерация не запускалась после изменения, пользовательский barrel не экспортирует `.svg-sprite` либо type server держит старый модуль | Запусти sprite-команду, проверь `export * from './.svg-sprite'`, затем typecheck; при необходимости перезапусти TypeScript server. | | SVG не загружается или URL неверен | Mode не совпадает со сборщиком, неверен Webpack `publicPath` либо кастомный loader перехватил asset | Сверь mode и build-команду, проверь Asset Modules/`publicPath`, исключи generated SVG из несовместимого loader. | | Next build расходится между SSR и браузером | Модуль сгенерирован для другого bundler/router или URL переписан вручную | Верни generated `new URL(...)`, выбери точный Next mode и перегенерируй. | | `color` не меняет многоцветную иконку | У иконки несколько переменных или она показана через ``/CSS background | Используй ``/`` и нужные `--icon-color-N`. | | Gradient/filter выглядит неверно | Автозамена цветов не гарантирует сложные paint servers | Изучи generated SVG; при необходимости отключи `replaceColors` для спрайта или упрости источник. | | Viewer пуст | Манифесты не созданы, glob/import не статический или неверен Client Component boundary | Сначала сгенерируй спрайты; для Vite используй literal glob, для Webpack/Next статические loaders, для App Router добавь `'use client'` только Viewer-странице. | -При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к конфигу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и защитные markers. +При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к config-файлу или каталогу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и защитные markers. diff --git a/skills/svg-sprites/src/ru/references/complex-svg.md b/skills/svg-sprites/src/ru/references/complex-svg.md index 0fce38d..acc304b 100644 --- a/skills/svg-sprites/src/ru/references/complex-svg.md +++ b/skills/svg-sprites/src/ru/references/complex-svg.md @@ -12,7 +12,7 @@ npm run sprite:file-manager ``` -Используй фактический package script нужного спрайта. Затем сравни source с `generated/sprite.svg` и manifest, не делая вывод только по успешному exit code. +Используй фактический package script нужного спрайта. Затем сравни source с `.svg-sprite/sprite.svg` и manifest, не делая вывод только по успешному exit code. Особого внимания требуют: @@ -36,9 +36,10 @@ npm run sprite:file-manager Все три опции по умолчанию `true` и применяются ко всему спрайту, не к отдельной иконке. ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'illustrations', transform: { removeSize: false, @@ -48,7 +49,7 @@ export default defineReactSpriteConfig({ }) ``` -Это config для одного из потенциально многих проектов спрайтов; его каталог не обязан совпадать с module/feature-каталогом. Для Next используй `defineNextSpriteConfig(...)` с тем же `transform`; для legacy он находится на верхнем уровне `defineLegacyConfig(...)`. +Это config для одного из потенциально многих sprite-модулей; его каталог не обязан совпадать с module/feature-каталогом. Для Next укажи соответствующий полный `mode` с тем же `transform`. ## Размеры и viewBox @@ -64,7 +65,7 @@ export default defineReactSpriteConfig({ Если физические размеры являются частью контракта иллюстрации, установи `removeSize: false` и проверь поведение component props. Не используй сохранение width/height как замену отсутствующему viewBox. -React и legacy compile оставляют root sprite `rootViewBox` выключенным; Next включает его. У каждой shape всё равно должен быть собственный корректный viewBox, который попадает в manifest и используется Viewer. +React compile оставляет root sprite `rootViewBox` выключенным; Next включает его. У каждой shape всё равно должен быть собственный корректный viewBox, который попадает в manifest и используется Viewer. ## Цвета @@ -128,7 +129,7 @@ transform: { ``` -Не создавай вручную `#folder open`. Используй generated component либо `manifest.ts`, где записаны `name` и фактический `id`. +Не создавай вручную `#folder open`. Используй generated component либо `.svg-sprite/svg-sprite.manifest.js`, где записаны `name` и фактический `id`. Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок `inputFiles` не является способом выбрать победителя. @@ -157,7 +158,7 @@ External stack fragment support и поведение paint servers могут 3. Открой generated sprite и найди shape по ID из manifest. 4. Статически сверь `viewBox`, IDs, `url(#...)`, colors и inline styles. 5. Если менялись target/pipeline или диагностируется runtime, собери production bundle и проверь внешний hashed SVG. -6. При наличии SpriteViewer, legacy `preview.html` и визуальных инструментов проверь default colors и каждую `--icon-color-N` отдельно. +6. При наличии SpriteViewer и визуальных инструментов проверь default colors и каждую `--icon-color-N` отдельно. 7. При наличии browser-инструментов и соответствующем runtime-риске проверь SSR/hydration для Next.js и целевые browsers для external fragments. 8. Не утверждай визуальную или a11y эквивалентность source и результата без доступных инструментов и фактического сравнения. @@ -172,4 +173,4 @@ External stack fragment support и поведение paint servers могут - Ручной fragment не работает для имени с пробелом: используй ID из manifest. - Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует. -Target-specific запуск и проверка описаны в [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), [next-pages.md](next-pages.md) и [legacy.md](legacy.md). +Target-specific запуск и проверка описаны в [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md) и [next-pages.md](next-pages.md). diff --git a/skills/svg-sprites/src/ru/references/legacy.md b/skills/svg-sprites/src/ru/references/legacy.md deleted file mode 100644 index ad9be89..0000000 --- a/skills/svg-sprites/src/ru/references/legacy.md +++ /dev/null @@ -1,132 +0,0 @@ -# Legacy mode: операционный reference - -## Когда открывать - -Открывай этот документ, если проект уже использует корневой `svg-sprites.config.ts`, централизованный output, несколько записей `sprites`, формат `symbol` или автономный `preview.html`. Не мигрируй такой проект на локальные React/Next modules без явного запроса; для плановой миграции открой [migration-1.md](migration-1.md). - -## Что отличает legacy - -- Один config управляет одним или несколькими спрайтами. -- Результаты записываются как `.sprite.svg` в общий `output`. -- Поддерживаются `stack` и `symbol`; default format равен `stack`. -- `preview` по умолчанию включён и создаёт общий `preview.html`. -- Нет generated React-компонента, manifest-модуля, локального `.gitignore` и защиты managed writer. -- Legacy paths в загруженном конфиге разрешаются относительно каталога, переданного CLI. - -## Конфиг и запуск - -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -`svg-sprites.config.ts`: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: [ - 'src/assets/logos/product.svg', - 'src/assets/shared/brand.svg', - ], - format: 'stack', - }, - ], -}) -``` - -В отличие от локальных React/Next config, каждый из которых описывает один из потенциально многих спрайтов, один legacy config сохраняет специфическую возможность управлять одним или несколькими entries спрайтов. - -Script: - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -Запусти `npm run sprites` из project root. - -CLI path указывает каталог с `svg-sprites.config.ts`. Не передавай сам файл. Для config в другом каталоге передай этот каталог явно. - -## Семантика input - -- Строковый `input` означает каталог; читаются только SVG первого уровня, отсортированные по имени. -- Массив `input` означает точный список файлов; пустой массив является ошибкой. -- Расширение проверяется case-sensitive по окончанию `.svg`. -- Для массива проверяется существование каждого файла. -- В отличие от локального React config, legacy entry не объединяет одновременно папку и список. Для смешанного набора перечисли все файлы либо раздели entries. -- `name` используется непосредственно в имени output-файла; выбирай уникальные безопасные имена и не создавай две entries с одинаковым output name. - -## Выбор формата и runtime - -`symbol` нужен для существующей интеграции через ``: - -```html - - - -``` - -`stack` подходит для ``, `` и CSS URL. Для нового React/Next-кода не строй local component поверх legacy output автоматически: сначала выбери соответствующий современный mode. - -CSS custom properties страницы управляют содержимым при ``, но не проникают в документ, загруженный как `` или `background-image`. `symbol` не предназначен для ``. - -Имена с пробелами и небезопасными символами получают hash ID `icon-<16 hex>`. Поэтому не предполагай, что basename всегда равен fragment; проверь generated SVG или preview. - -## Preview - -При `preview: true` после всех SVG создаётся `output/preview.html`. Он содержит данные и inline-копии generated sprites. При диагностике runtime и наличии браузерных инструментов его можно открыть для проверки числа иконок, `viewBox` и `--icon-color-N`. - -При `preview: false` новый preview не создаётся. Генератор не является очистителем произвольных файлов output: если старый `preview.html` больше не нужен, удаляй его только после подтверждения, что это прежний generated artifact. - -## Проверка - -```bash -npm run sprites -npm run typecheck -``` - -Генерация и typecheck проекта — быстрые обязательные проверки. Проверь для каждой entry: - -- существует `public/sprites/.sprite.svg`; -- в логе совпадают format и число иконок; -- ожидаемые `` либо вложенные `` присутствуют; -- `preview.html` создан только при включённом preview; -- SVG со сложными `defs` проверены по [complex-svg.md](complex-svg.md). - -Production build и браузер/Network запускай дополнительно только при изменении output/public pipeline или диагностике runtime. Тогда проверь URL реального приложения с учётом public base path и, если доступны визуальные инструменты, содержимое `preview.html`; не утверждай визуальный или a11y результат без такой проверки. - -## Типовые ошибки - -- `Config file not found`: CLI path не является каталогом с `svg-sprites.config.ts`. -- `Config file must have a default export`: экспортируй config через `defineLegacyConfig(...)`. -- Deprecated `mode`: поле `sprites[].mode` переименовано в `format`. -- `sprites must be a non-empty array`: legacy config не допускает пустую конфигурацию. -- `Input directory does not exist` или `SVG file does not exist`: помни, что paths считаются от каталога config при загрузке CLI. -- Вложенные SVG не найдены: directory scan не рекурсивен. -- Preview template not found при запуске из исходников пакета: собери package preview template; в опубликованном пакете он входит в distribution. -- Цвет не меняется в ``: это изолированный SVG document; используй `` либо заранее заданные цвета. -- Старые output-файлы остались после удаления entry: legacy generator записывает текущие результаты, но не владеет очисткой всего output. - -Программные варианты запуска и различия path resolution описаны в [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/references/migration-1.md b/skills/svg-sprites/src/ru/references/migration-1.md deleted file mode 100644 index 86488d5..0000000 --- a/skills/svg-sprites/src/ru/references/migration-1.md +++ /dev/null @@ -1,185 +0,0 @@ -# Миграция с legacy API 0.1.x на latest: операционный reference - -## Когда открывать - -Открывай этот документ только при установленной или ранее использовавшейся версии `0.1.x`, старых вызовах `svg-sprites` без `--mode`, `defineConfig`, `generate`, `loadConfig`, поле `sprites[].mode`, `publicPath` или общем компоненте ``. Не выполняй миграцию как побочный рефакторинг. - -## Инвентаризация до правок - -Сначала зафиксируй текущий контракт проекта: - -```bash -npm ls @gromlab/svg-sprites -``` - -Перед обновлением config и scripts установи актуальный пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Найди и прочитай: - -- `svg-sprites.config.ts` и `svg-sprite.config.ts`; -- scripts, вызывающие `svg-sprites`; -- импорты `defineConfig`, `generate`, `loadConfig`, `SvgSprite`; -- ссылки на `publicPath`, `.sprite.svg`, старый generated-компонент и preview; -- правила `.gitignore`, SVG loaders и CI generation steps. - -До изменений реши один из двух путей. Не смешивай их в одном CLI-вызове. - -## Путь A: сохранить централизованный legacy pipeline - -Выбирай его, если приложение зависит от общего `public/sprites`, `symbol`, статических URL или `preview.html`. - -Маппинг API: - -| Legacy API 0.1.x | Latest API | -|---|---| -| `defineConfig` | `defineLegacyConfig` | -| `sprites[].mode` | `sprites[].format` | -| `generate` | `generateLegacy` | -| `loadConfig` | `loadLegacyConfig` | -| CLI без mode | `svg-sprites --mode legacy ` в package script | - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy ." - } -} -``` - -Запусти его командой `npm run sprites`. - -`publicPath` и генерация старого общего React-компонента удалены. Если приложение использовало этот компонент, legacy сохранит SVG asset, но React wrapper придётся заменить отдельно. Полный legacy workflow находится в [legacy.md](legacy.md). - -## Путь B: перейти на локальные React/Next modules - -Выбирай его, если нужны типизированные компоненты, hashed assets сборщика, Server Components или разбиение большого набора. - -Для каждого нужного спрайта создай каталог проекта с локальным `svg-sprite.config.ts` и, при использовании папки, `icons/`. Каталог не обязан быть module/feature-каталогом; каждый config описывает один из потенциально многих спрайтов: - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'global', - inputFolder: './icons', - inputFiles: ['../../../shared/icons/check.svg'], -}) -``` - -Для Next.js используй `defineNextSpriteConfig(...)`. Затем выбери ровно один mode: - -| Среда | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next App + Turbopack | `next@app/turbopack` | -| Next App + Webpack 5 | `next@app/webpack` | -| Next Pages + Turbopack | `next@pages/turbopack` | -| Next Pages + Webpack 5 | `next@pages/webpack` | - -Пример: - -```json -{ - "scripts": { - "sprite:global": "svg-sprites --mode react@vite src/ui/global/svg-sprite", - "sprites": "npm run sprite:global" - } -} -``` - -Запусти его командой `npm run sprite:global`. - -Замени старый generic component: - -```tsx -// Было - - -// Стало при name: 'global' - -``` - -Новый модуль сам создаёт локальный `.gitignore`, `index.ts`, `manifest.ts` и `generated/`. SVG публикует bundler; не сохраняй старый `publicPath` и не копируй sprite в `public`. - -Выбери детальную процедуру: [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md) или [next-pages.md](next-pages.md). - -## Изменения программного API - -Пакет теперь только ESM: - -```ts -import { compileSpriteContent, generateLegacy } from '@gromlab/svg-sprites' -``` - -Не используй `require()`. `compileSpriteContent(...)` теперь возвращает `Promise`, а не Node-specific `Buffer` declaration: - -```ts -const bytes = await compileSpriteContent(folder) -await fs.promises.writeFile('sprite.svg', bytes) -``` - -Если старый код вызывал `generate`, замени его на `generateLegacy`; для local modules используй `generateReactSprite` или `generateNextSprite`. Точные сигнатуры см. в [programmatic-api.md](programmatic-api.md). - -## Безопасный порядок миграции - -1. Зафиксируй список текущих icon names, formats, public URLs и цветовых ожиданий. -2. При миграции импортов обнови dependency до latest и config API, но не удаляй старые artifacts до успешной новой генерации. -3. Добавь явный mode и каталог во все local/CI scripts. -4. Сгенерируй новый output. -5. Замени imports и JSX usages; проверь TypeScript union icon names. -6. Если менялись target/pipeline или диагностируется runtime, проверь asset URLs и SSR/SSG; визуальный результат оценивай только доступными для этого инструментами. -7. Только после этого удали подтверждённые старые generated-файлы и устаревшие ignore/loader rules. - -Не удаляй целиком каталог, в котором могут находиться исходные SVG или пользовательские файлы. Актуальный writer отказывается перезаписывать файлы без generated marker; не обходи эту защиту. - -## Проверка после миграции - -Минимум для npm-проекта: - -```bash -npm run sprites -npm run typecheck -``` - -Используй реальные имена scripts проекта. Генерация и typecheck — быстрые обязательные проверки. Production build, браузер и Network добавляй, только если миграция меняет target/pipeline или диагностируется runtime. Проверь: - -- ни один script не вызывает CLI без `--mode` и path; -- config helper и CLI mode относятся к одному pipeline; -- local manifest содержит ожидаемый target, а legacy output содержит ожидаемый format; -- старые `publicPath`, `sprites[].mode` и imports удалённого API отсутствуют; -- generated-файлы создаются до typecheck/build в чистом checkout; -- каждый старый icon name доступен либо намеренно переименован; -- цвета и сложные SVG исследованы по [complex-svg.md](complex-svg.md), без утверждений о визуальной или a11y корректности при отсутствии инструментов; -- `SpriteViewer` заменил отдельный preview только для React/Next; legacy по-прежнему может использовать `preview.html`. - -## Типовые ошибки - -- `Missing required argument: --mode`: старый script не обновлён. -- `React mode requires a target` или `Next.js mode requires a router and bundler`: указан сокращённый mode вместо полного ключа. -- Deprecated `mode`: в legacy entry осталось `sprites[].mode`. -- `icons was renamed to inputFolder`: старое поле перенесено в local React/Next config без переименования. -- `require() of ES Module`: build script не переведён на ESM/import. -- Иконки исчезли: local `inputFolder` сканируется нерекурсивно либо shared files не добавлены через `inputFiles`. -- Runtime ищет старый `/sprites/...`: JSX/CSS usage не переведён на generated component или новый asset URL. -- Один sprite генерируется разными targets: CI/local scripts не согласованы; оставь один target, соответствующий реальному bundler. diff --git a/skills/svg-sprites/src/ru/references/next-app.md b/skills/svg-sprites/src/ru/references/next-app.md index 6a43480..5f97928 100644 --- a/skills/svg-sprites/src/ru/references/next-app.md +++ b/skills/svg-sprites/src/ru/references/next-app.md @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@app/turbopack', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', @@ -41,7 +42,7 @@ export default defineNextSpriteConfig({ ``` - Каталог выбирает проект конкретного спрайта и не обязан быть module/feature-каталогом. Каждый config описывает один из потенциально многих спрайтов приложения. -- Конфиг называется в единственном числе: `svg-sprite.config.ts`. +- `svg-sprite.config.ts` — рекомендуемое имя; CLI принимает явно переданный `.ts`, `.js` или `.json` файл с любым именем. - Пути считаются от его каталога; `inputFolder` сканируется нерекурсивно. - Локальная папка и `inputFiles` объединяются. Одинаковые basename разных файлов запрещены. - Если имя не задано, оно выводится из каталога: для папки `svg-sprite` берётся имя родительской папки. Явное `name` надёжнее при перемещениях. @@ -56,7 +57,7 @@ export default defineNextSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -84,7 +85,7 @@ export default function Page() { `width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Не добавляй Client Component boundary только ради иконки. Generated module формирует asset URL через статический `new URL('./sprite.svg', import.meta.url).href`; один механизм используется при SSR и в браузере. -Не импортируй из `generated/` и не перемещай SVG в `public`. Управляемые файлы (`generated/`, `index.ts`, `manifest.ts`, `.gitignore`) перегенерируются. +Не перемещай SVG в `public`. Генератор перезаписывает `.svg-sprite` и `.gitignore`; корневой `index.ts` принадлежит приложению и может содержать `export * from './.svg-sprite'`. ## SpriteViewer @@ -124,7 +125,7 @@ npm run build После обязательных генерации и typecheck проверь: -- `manifest.ts` содержит точный target `next@app/turbopack` либо `next@app/webpack`; +- `.svg-sprite/svg-sprite.manifest.js` содержит точный target `next@app/turbopack` либо `next@app/webpack`; - server page компилируется без добавления `'use client'`; При условной production/runtime-проверке проверь также: @@ -140,7 +141,7 @@ npm run build - Сборка проходит на одном bundler, а runtime asset ломается на другом: перегенерируй тем target, которым реально выполняется build. - Выбран Webpack, но build ушёл в Turbopack: используй Webpack-команду проекта и `next@app/webpack`. - Viewer вызывает ошибку Server Component: файл с Viewer должен иметь `'use client'`; generated icon component этого не требует. -- `React config file not found`: команда получила путь к `app/`, `icons/` или конфигу вместо каталога спрайта. +- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу. - Две CI jobs генерируют разные target в одном checkout: раздели каталоги или обеспечь один согласованный target на job. - Asset не найден под `basePath`/CDN: проверяй Next asset handling и deployment config, не подменяй generated URL вручную. - Ошибка package subpath types: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. diff --git a/skills/svg-sprites/src/ru/references/next-pages.md b/skills/svg-sprites/src/ru/references/next-pages.md index f208529..d305cda 100644 --- a/skills/svg-sprites/src/ru/references/next-pages.md +++ b/skills/svg-sprites/src/ru/references/next-pages.md @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineNextSpriteConfig({ +export default defineSpriteConfig({ + mode: 'next@pages/webpack', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', @@ -53,7 +54,7 @@ export default defineNextSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -79,7 +80,7 @@ export function getServerSideProps() { `width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Компонент работает при SSR, SSG и client navigation. Он использует `new URL('./sprite.svg', import.meta.url).href`, чтобы Next выпустил внешний hashed asset. Не копируй generated SVG в `public` и не конструируй URL вручную. -Импортируй компонент и типы только из локального `svg-sprite/index.ts`. Не редактируй `generated/`, `index.ts`, `manifest.ts` и созданный `.gitignore`. +Импортируй компонент и типы из локального пользовательского `svg-sprite/index.ts`, который переэкспортирует `.svg-sprite`. Не редактируй `.svg-sprite` и созданный `.gitignore`. ## SpriteViewer @@ -115,7 +116,7 @@ npm run build После обязательных генерации и typecheck проверь: -- `manifest.ts` содержит `next@pages/turbopack` либо `next@pages/webpack`; +- `.svg-sprite/svg-sprite.manifest.js` содержит `next@pages/turbopack` либо `next@pages/webpack`; - `getServerSideProps`/`getStaticProps` не импортируют package React Viewer entry. После условного production build и только при наличии браузерных инструментов проверь SSR route и переход на него: diff --git a/skills/svg-sprites/src/ru/references/programmatic-api.md b/skills/svg-sprites/src/ru/references/programmatic-api.md index 917c99d..93a9686 100644 --- a/skills/svg-sprites/src/ru/references/programmatic-api.md +++ b/skills/svg-sprites/src/ru/references/programmatic-api.md @@ -1,244 +1,41 @@ # Программный API: операционный reference -## Обязательная установка +Используй `generateSprite(source, overrides?)` как основной Node.js API. -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Программные imports требуют локальной development dependency, установленной выше. Этот reference описывает API установленной версии пакета. - -## Когда открывать - -Открывай этот документ, если генерация запускается из ESM build script, monorepo orchestration, собственного CLI или теста, а не напрямую package script. Для обычной интеграции предпочитай CLI references: [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), [next-pages.md](next-pages.md) или [legacy.md](legacy.md). - -## Runtime и TypeScript - -Основная точка входа только ESM и не импортирует React: +## Из config-файла ```ts -import { generateReactSprite } from '@gromlab/svg-sprites' +import { generateSprite } from '@gromlab/svg-sprites' + +await generateSprite('src/ui/icons/svg-sprite.config.ts') ``` -Требуется Node.js 18+. Не используй `require()`. Для package subpath `@gromlab/svg-sprites/react` нужен TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. +`source` должен указывать на конкретный `.ts`, `.js` или `.json` файл. Имя файла произвольное; генератор не выполняет discovery. Корнем sprite-модуля и базой относительных путей становится каталог этого файла. -## React module +## Без config-файла ```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -Сигнатура target: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -`root` разрешается относительно текущего `process.cwd()`. Это выбранный каталог конкретного спрайта, а не обязательный module/feature-каталог. Внутри него функция загружает только `svg-sprite.config.ts`, объединяет `inputFolder` и `inputFiles`, компилирует `stack` и безопасно обновляет managed-файлы. Каждый такой config описывает один из потенциально многих спрайтов. - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -Передавай target явно. Значение вне union синхронно вызывает `Unsupported React asset target`. - -## Next.js module - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -Результат содержит поля React result, target `next@/`, а также `router` и `bundler`. Next generation всегда использует `stack` и включает root `viewBox`. Выбранные options должны совпадать с фактической Next build command. - -## Config helpers и загрузчики - -Helpers только возвращают объект и дают autocomplete; они не загружают файлы и не запускают валидацию: - -```ts -import { - defineLegacyConfig, - defineNextSpriteConfig, - defineReactSpriteConfig, - loadLegacyConfig, - loadReactSpriteConfig, -} from '@gromlab/svg-sprites' -``` - -```ts -const reactConfig = await loadReactSpriteConfig( - 'src/ui/file-manager/svg-sprite', -) - -const legacyConfig = await loadLegacyConfig('.') -``` - -`loadReactSpriteConfig(root)` разрешает `inputFolder`/`inputFiles` от `root` и возвращает нормализованный config. Несмотря на название, тот же loader используется Next mode. - -`loadLegacyConfig(cwd)` ищет `cwd/svg-sprites.config.ts`, валидирует его и превращает `output` и `sprites[].input` в absolute paths относительно `cwd`. - -## Legacy generation - -Прямой вызов: - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], +await generateSprite('src/ui/icons', { + mode: 'react@vite', + name: 'app', + inputFolder: './icons', }) ``` -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` +Каталог включает config-less режим. После объединения настроек `mode` обязателен. -Важный path nuance: `generateLegacy(config)` не знает местоположение config-файла. Относительные `output` и `input` разрешаются от текущего `process.cwd()`. Если semantics должны совпасть с CLI для config в другом каталоге, сначала вызови loader: +## Overrides ```ts -const config = await loadLegacyConfig('config/sprites') -const results = await generateLegacy(config) -``` - -## Низкоуровневая компиляция - -Основные exports: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -Минимальный in-memory пример: - -```ts -import { compileSpriteContent, resolveSpriteEntry } from '@gromlab/svg-sprites' - -const folder = resolveSpriteEntry({ - name: 'icons', - input: 'src/assets/icons', - format: 'stack', +await generateSprite('src/ui/icons/custom.json', { + mode: 'react@webpack', + inputFiles: ['../../shared/search.svg'], + transform: { addTransition: false }, }) - -const bytes = await compileSpriteContent( - folder, - { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - { rootViewBox: false }, -) ``` -`compileSpriteContent` возвращает `Promise` и не пишет на диск. `compileSprite(folder, outputDir, transform?, options?)` создаёт output directory и записывает `.sprite.svg`. +Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `inputFiles` заменяет массив из config. -`CompileSpriteOptions.rootViewBox` по умолчанию `false`; стандартный Next preset передаёт `true`, React и legacy оставляют `false`. Не меняй эту опцию в попытке заменить target: она не определяет способ публикации asset. +Специализированные `generateReactSprite` и `generateNextSprite` оставлены как совместимые обёртки, но для нового кода предпочитай `generateSprite`. -`resolveSpriteEntry` и `resolveSprites` разрешают source paths относительно `process.cwd()`, проверяют existence и читают directory только на первом уровне. Они не применяют semantics локального `inputFolder + inputFiles`; для стандартных React/Next modules используй high-level generators. - -`createShapeTransform(options)` возвращает callback transform для `svg-sprite`. Это integration primitive, а не функция `string -> string`. - -`generatePreview(results, outputDir)` ожидает, что `results[].spritePath` уже существует, и пишет `preview.html` на основе package template. Не вызывай его до компиляции. - -## React debug runtime - -Viewer находится в отдельной client entry: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { - SpriteManifest, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -`SpriteViewerSources` принимает массив manifest/loader или record, например результат Vite `import.meta.glob`. Loader может вернуть manifest напрямую либо module с `default`/`spriteManifest`. Невалидный module завершится ошибкой `The loaded module does not export a valid SVG sprite manifest.` - -Package React entry содержит `'use client'`; не импортируй его из server-only build scripts и production icon modules. Generated production component находится в выбранном каталоге спрайта. - -## Оркестрация нескольких модулей - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const roots = [ - 'src/ui/global/svg-sprite', - 'src/ui/file-manager/svg-sprite', -] - -const results = await Promise.all( - roots.map((root) => generateReactSprite(root, 'vite')), -) - -if (results.some((result) => result.iconCount === 0)) { - throw new Error('Пустой sprite module') -} -``` - -Не запускай разные targets параллельно для одного root: оба вызова владеют одинаковыми generated paths. - -## Проверка и обработка ошибок - -Запусти программную генерацию и typecheck проекта — это быстрые обязательные проверки. После вызова проверь `result.target`, `iconCount`, existence `spritePath`/`manifestPath`. Реальную bundler build и browser/Network-проверку добавляй только при изменении target/pipeline или диагностике runtime; не утверждай визуальный или a11y результат без доступных инструментов. - -Типовые причины failure: - -- unsupported target/router/bundler передан как unchecked string; -- `process.cwd()` отличается в monorepo runner, поэтому relative root указывает не туда; -- config не имеет default export; -- явно заданная `inputFolder` отсутствует; -- два source-файла дают одинаковый fragment ID; -- custom orchestration пытается перезаписать пользовательский файл в managed path; -- `generatePreview` не находит template при запуске из несобранных исходников package; -- сложный SVG требует отключения отдельных transforms по [complex-svg.md](complex-svg.md). +Для загрузки и собственной оркестрации доступны `loadSpriteConfig`, `validateSpriteConfig`, `resolveSpriteConfig`, `compileSpriteContent` и `createShapeTransform`. diff --git a/skills/svg-sprites/src/ru/references/react-vite.md b/skills/svg-sprites/src/ru/references/react-vite.md index cfaf867..60812d9 100644 --- a/skills/svg-sprites/src/ru/references/react-vite.md +++ b/skills/svg-sprites/src/ru/references/react-vite.md @@ -9,7 +9,7 @@ 1. Проверь `package.json`: должны быть React, Vite и фактические команды `dev`, `build`, `typecheck`. 2. Найди существующие `svg-sprite.config.ts` и scripts с `svg-sprites`. Не создавай второй каталог для уже существующего спрайта. 3. Выбери целевой каталог для конкретного спрайта. Это не обязан быть каталог module или feature: генератор принимает каталог с config, а не путь к самому config или `icons/`. -4. Не редактируй вручную `generated/`, `index.ts`, `manifest.ts` и локальный `.gitignore`: ими владеет генератор. +4. Не редактируй вручную `.svg-sprite` и локальный `.gitignore`: ими владеет генератор. Корневой `index.ts` принадлежит приложению. Минимальная структура: @@ -32,9 +32,10 @@ npm install --save-dev @gromlab/svg-sprites `src/ui/file-manager/svg-sprite/svg-sprite.config.ts`: ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@vite', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', @@ -58,7 +59,7 @@ export default defineReactSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -95,7 +96,7 @@ export const availableIcons = fileManagerIconNames ``` -Не делай deep import из `generated/`: структура generated-файлов не является точкой интеграции. +Для обычного импорта используй пользовательский barrel с `export * from './.svg-sprite'`; остальные файлы скрытого каталога считаются внутренними. ## Нюанс target @@ -110,14 +111,14 @@ Query `?no-inline` обязателен: он не даёт Vite преврат Для низкоуровневого `` применяй тот же механизм: ```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' +import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline' ``` -Ручной fragment `#check` безопасен только для имён вида `^[a-zA-Z][a-zA-Z0-9_-]*$`. Для пробелов и других символов generated-компонент использует стабильный hash ID; точный ID находится в `manifest.ts`. +Ручной fragment `#check` безопасен только для имён вида `^[a-zA-Z][a-zA-Z0-9_-]*$`. Для пробелов и других символов generated-компонент использует стабильный hash ID; точный ID находится в `.svg-sprite/svg-sprite.manifest.js`. ## SpriteViewer @@ -128,7 +129,7 @@ import { SpriteViewer } from '@gromlab/svg-sprites/react' import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', ) export function IconsDebugPage() { @@ -136,7 +137,7 @@ export function IconsDebugPage() { } ``` -Аргумент `import.meta.glob` должен оставаться строковым литералом. Генерация обязана завершиться до старта Vite, иначе новые `manifest.ts` не попадут в glob. +Аргумент `import.meta.glob` должен оставаться строковым литералом. Генерация обязана завершиться до старта Vite, иначе новые manifests не попадут в glob. ## Проверка результата @@ -147,18 +148,18 @@ npm run typecheck Это быстрые обязательные проверки. Проверь generated-файлы статически: -- публичный `index.ts` экспортирует компонент, props, style, union имени и runtime-массив; -- `manifest.ts` содержит `target: "vite"`, `format: "stack"` и ожидаемое число иконок; +- пользовательский `index.ts` переэкспортирует компонент, props, style, union имени и runtime-массив из `.svg-sprite`; +- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "vite"`, `format: "stack"` и ожидаемое число иконок; Если менялись target, asset pipeline или диагностируется runtime, дополнительно запусти production build. При наличии браузерных инструментов проверь Network: отдельный `.svg` asset вместо `data:image/svg+xml`, успешный URL и ``. Для сложных цветов, `defs` и размеров следуй [complex-svg.md](complex-svg.md), не утверждая визуальный или a11y результат без доступных инструментов. ## Типовые ошибки -- `React config file not found`: в команду передан путь к `icons/` или к файлу; передай каталог, содержащий `svg-sprite.config.ts`. -- `React mode requires a target`: использован `--mode react`; нужен ровно `react@vite`. +- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу. +- `Sprite mode is required`: добавь `mode: 'react@vite'` в config либо передай `--mode react@vite`. - Иконки нет в autocomplete: проверь case-sensitive окончание `.svg`, нерекурсивное расположение и повторно запусти генерацию до typecheck. - `Refusing to overwrite a user file`: не удаляй marker и не обходи writer; перенеси пользовательский файл или выбери другой sprite-каталог. -- Viewer пуст: проверь строковый glob, существование generated `manifest.ts` и порядок запуска `predev`. +- Viewer пуст: проверь строковый glob, существование `.svg-sprite/svg-sprite.manifest.js` и порядок запуска `predev`. - SVG оказался inline: проверь, что модуль сгенерирован target `vite` и импорт сохранил `?no-inline`. - TypeScript не разрешает package subpath: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. diff --git a/skills/svg-sprites/src/ru/references/react-webpack.md b/skills/svg-sprites/src/ru/references/react-webpack.md index 702118d..601b1c2 100644 --- a/skills/svg-sprites/src/ru/references/react-webpack.md +++ b/skills/svg-sprites/src/ru/references/react-webpack.md @@ -30,9 +30,10 @@ npm install --save-dev @gromlab/svg-sprites ``` ```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' +import { defineSpriteConfig } from '@gromlab/svg-sprites' -export default defineReactSpriteConfig({ +export default defineSpriteConfig({ + mode: 'react@webpack', name: 'file-manager', description: 'Иконки файлового менеджера', inputFolder: './icons', @@ -49,7 +50,7 @@ export default defineReactSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", + "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "predev": "npm run sprite:file-manager", "prebuild": "npm run sprite:file-manager", "pretypecheck": "npm run sprite:file-manager" @@ -74,7 +75,7 @@ export function OpenFolderButton() { } ``` -`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Публичный entry также экспортирует `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName` и `fileManagerIconNames`. Не импортируй файлы напрямую из `generated/` и не редактируй `generated/`, `index.ts`, `manifest.ts` или созданный `.gitignore`. +`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Пользовательский barrel также экспортирует `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName` и `fileManagerIconNames`. Не редактируй `.svg-sprite` или созданный `.gitignore`. ## Нюанс Webpack target @@ -88,7 +89,7 @@ Webpack 5 распознаёт его как Asset Module и заменяет н - не оборачивай путь в переменную и не меняй generated expression; - убедись, что Babel/TypeScript не преобразует `import.meta.url` до Webpack; -- не позволяй `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общему SVG rule перехватить `svg-sprite/generated/sprite.svg`; +- не позволяй `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общему SVG rule перехватить `svg-sprite/.svg-sprite/sprite.svg`; - при custom rule либо исключи generated sprite из component/raw loader, либо добавь отдельное правило `type: 'asset/resource'`; - проверь `output.publicPath`, особенно при CDN, subpath deployment и dev-server. @@ -96,7 +97,7 @@ Webpack 5 распознаёт его как Asset Module и заменяет н ```js { - test: /svg-sprite[\\/]generated[\\/]sprite\.svg$/, + test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/, type: 'asset/resource', } ``` @@ -131,7 +132,7 @@ npm run typecheck Это быстрые обязательные проверки. После генерации проверь: -- `manifest.ts` содержит `target: "webpack"` и `format: "stack"`; +- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "webpack"` и `format: "stack"`; - generated component содержит `new URL('./sprite.svg', import.meta.url).href`; Production build и браузер/Network нужны дополнительно, если менялись target, Webpack asset rules, `publicPath`/deployment pipeline или диагностируется runtime. Тогда проверь отдельный hashed SVG asset, HTTP(S)-URL `.svg#id`, content type, fragment ID и корректный CDN/subpath URL. Не утверждай визуальную или a11y корректность без доступных инструментов. @@ -145,7 +146,7 @@ Production build и браузер/Network нужны дополнительно - URL ведёт на неверный host/subpath: исправь `output.publicPath` и настройки runtime deployment, не generated-файл. - `import.meta` не поддержан: проверь, что сборка действительно Webpack 5 и промежуточный transpiler сохраняет выражение. - Viewer не загружает manifest: проверь literal path, chunk loading и наличие генерации до компиляции. -- `Refusing to overwrite a user file`: каталог уже содержит пользовательский `index.ts`, `manifest.ts`, `.gitignore` или файл в `generated/`; перенеси его, не обходи защиту. +- `Refusing to overwrite a user file`: каталог уже содержит пользовательский `.gitignore` или файл в `.svg-sprite`; перенеси его, не обходи защиту. - Иконка не меняет цвет: используй `color` для монохромной либо `--icon-color-N` через ``; CSS страницы не проникает внутрь ``. Для custom build orchestration см. [programmatic-api.md](programmatic-api.md). From 632b450c8b10267c1a32a321fb297b9850f83b7b Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Mon, 13 Jul 2026 20:08:31 +0300 Subject: [PATCH 03/11] =?UTF-8?q?test:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BF=D0=BB=D0=BE=D1=89=D0=B0=D0=B4=D0=BA?= =?UTF-8?q?=D1=83=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - добавлены consumer fixtures для React и Next.js - добавлены typecheck, production build и браузерные smoke-тесты - добавлена интеграционная проверка в CI --- .github/workflows/ci.yml | 33 + integration/.gitignore | 16 + integration/README.md | 67 + integration/apps/angular/angular.json | 53 + integration/apps/angular/package.json | 27 + integration/apps/angular/src/app.component.ts | 20 + integration/apps/angular/src/index.html | 11 + integration/apps/angular/src/main.ts | 5 + integration/apps/angular/src/styles.css | 16 + .../apps/angular/svg-sprites.config.ts | 11 + integration/apps/angular/tsconfig.app.json | 9 + integration/apps/angular/tsconfig.json | 27 + integration/apps/astro/package.json | 20 + integration/apps/astro/src/pages/index.astro | 44 + integration/apps/astro/svg-sprites.config.ts | 11 + integration/apps/astro/tsconfig.json | 3 + .../apps/next-app-turbopack/app/layout.tsx | 11 + .../apps/next-app-turbopack/app/page.tsx | 18 + .../apps/next-app-turbopack/app/style.css | 10 + .../generated-contract.typecheck.tsx | 18 + .../apps/next-app-turbopack/next-env.d.ts | 6 + .../apps/next-app-turbopack/next.config.mjs | 10 + .../apps/next-app-turbopack/package.json | 24 + .../next-app-turbopack/src/sprite/.gitignore | 2 + .../next-app-turbopack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/next-app-turbopack/tsconfig.json | 36 + .../apps/next-app-webpack/app/layout.tsx | 11 + .../apps/next-app-webpack/app/page.tsx | 18 + .../apps/next-app-webpack/app/style.css | 10 + .../apps/next-app-webpack/next-env.d.ts | 6 + .../apps/next-app-webpack/next.config.mjs | 10 + .../apps/next-app-webpack/package.json | 24 + .../next-app-webpack/src/sprite/.gitignore | 2 + .../apps/next-app-webpack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/next-app-webpack/tsconfig.json | 36 + .../apps/next-pages-turbopack/next-env.d.ts | 6 + .../apps/next-pages-turbopack/next.config.mjs | 10 + .../apps/next-pages-turbopack/package.json | 24 + .../apps/next-pages-turbopack/pages/_app.tsx | 7 + .../apps/next-pages-turbopack/pages/index.tsx | 18 + .../apps/next-pages-turbopack/pages/style.css | 10 + .../src/sprite/.gitignore | 2 + .../next-pages-turbopack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/next-pages-turbopack/tsconfig.json | 35 + .../apps/next-pages-webpack/next-env.d.ts | 6 + .../apps/next-pages-webpack/next.config.mjs | 10 + .../apps/next-pages-webpack/package.json | 24 + .../apps/next-pages-webpack/pages/_app.tsx | 7 + .../apps/next-pages-webpack/pages/index.tsx | 18 + .../apps/next-pages-webpack/pages/style.css | 10 + .../next-pages-webpack/src/sprite/.gitignore | 2 + .../next-pages-webpack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + .../apps/next-pages-webpack/tsconfig.json | 35 + integration/apps/nuxt/app/app.vue | 32 + integration/apps/nuxt/nuxt.config.ts | 4 + integration/apps/nuxt/package.json | 22 + integration/apps/nuxt/svg-sprites.config.ts | 11 + integration/apps/react-vite/index.html | 12 + integration/apps/react-vite/package.json | 24 + integration/apps/react-vite/src/main.tsx | 22 + .../apps/react-vite/src/sprite/.gitignore | 2 + .../apps/react-vite/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/react-vite/src/style.css | 10 + integration/apps/react-vite/src/vite-env.d.ts | 1 + integration/apps/react-vite/tsconfig.json | 13 + integration/apps/react-vite/vite.config.ts | 6 + integration/apps/react-webpack/package.json | 29 + integration/apps/react-webpack/src/env.d.ts | 6 + integration/apps/react-webpack/src/main.tsx | 22 + .../apps/react-webpack/src/sprite/.gitignore | 2 + .../apps/react-webpack/src/sprite/index.ts | 1 + .../src/sprite/svg-sprite.config.ts | 8 + integration/apps/react-webpack/src/style.css | 10 + integration/apps/react-webpack/tsconfig.json | 12 + .../apps/react-webpack/webpack.config.js | 52 + integration/apps/solid-vite/index.html | 12 + integration/apps/solid-vite/package.json | 21 + integration/apps/solid-vite/src/main.tsx | 21 + integration/apps/solid-vite/src/style.css | 16 + integration/apps/solid-vite/src/vite-env.d.ts | 1 + .../apps/solid-vite/svg-sprites.config.ts | 11 + integration/apps/solid-vite/tsconfig.json | 14 + integration/apps/solid-vite/vite.config.ts | 6 + integration/apps/svelte-vite/index.html | 12 + integration/apps/svelte-vite/package.json | 22 + integration/apps/svelte-vite/src/App.svelte | 30 + integration/apps/svelte-vite/src/app.d.ts | 6 + integration/apps/svelte-vite/src/main.ts | 7 + .../apps/svelte-vite/src/vite-env.d.ts | 2 + .../apps/svelte-vite/svg-sprites.config.ts | 11 + integration/apps/svelte-vite/tsconfig.json | 14 + integration/apps/svelte-vite/vite.config.ts | 6 + integration/apps/sveltekit/package.json | 25 + integration/apps/sveltekit/src/app.html | 11 + .../apps/sveltekit/src/routes/+page.svelte | 34 + integration/apps/sveltekit/svelte.config.js | 7 + .../apps/sveltekit/svg-sprites.config.ts | 11 + integration/apps/sveltekit/tsconfig.json | 14 + integration/apps/sveltekit/vite.config.ts | 6 + integration/apps/vanilla-vite/index.html | 12 + integration/apps/vanilla-vite/package.json | 17 + integration/apps/vanilla-vite/src/main.ts | 8 + integration/apps/vanilla-vite/src/style.css | 16 + .../apps/vanilla-vite/src/vite-env.d.ts | 1 + .../apps/vanilla-vite/svg-sprites.config.ts | 11 + integration/apps/vanilla-vite/tsconfig.json | 11 + integration/apps/vue-vite/index.html | 12 + integration/apps/vue-vite/package.json | 22 + integration/apps/vue-vite/src/App.vue | 32 + integration/apps/vue-vite/src/main.ts | 5 + integration/apps/vue-vite/src/vite-env.d.ts | 1 + .../apps/vue-vite/svg-sprites.config.ts | 11 + integration/apps/vue-vite/tsconfig.json | 12 + integration/apps/vue-vite/vite.config.ts | 6 + integration/fixtures/icons/check.svg | 3 + integration/fixtures/icons/duotone.svg | 4 + integration/package-lock.json | 24458 ++++++++++++++++ integration/package.json | 20 + integration/playwright.config.ts | 16 + integration/scripts/apps.mjs | 8 + integration/scripts/run-workspaces.mjs | 31 + integration/scripts/serve-static.mjs | 45 + integration/tests/apps.spec.ts | 134 + integration/types/pngjs.d.ts | 8 + package.json | 5 + 130 files changed, 26424 insertions(+) create mode 100644 integration/.gitignore create mode 100644 integration/README.md create mode 100644 integration/apps/angular/angular.json create mode 100644 integration/apps/angular/package.json create mode 100644 integration/apps/angular/src/app.component.ts create mode 100644 integration/apps/angular/src/index.html create mode 100644 integration/apps/angular/src/main.ts create mode 100644 integration/apps/angular/src/styles.css create mode 100644 integration/apps/angular/svg-sprites.config.ts create mode 100644 integration/apps/angular/tsconfig.app.json create mode 100644 integration/apps/angular/tsconfig.json create mode 100644 integration/apps/astro/package.json create mode 100644 integration/apps/astro/src/pages/index.astro create mode 100644 integration/apps/astro/svg-sprites.config.ts create mode 100644 integration/apps/astro/tsconfig.json create mode 100644 integration/apps/next-app-turbopack/app/layout.tsx create mode 100644 integration/apps/next-app-turbopack/app/page.tsx create mode 100644 integration/apps/next-app-turbopack/app/style.css create mode 100644 integration/apps/next-app-turbopack/generated-contract.typecheck.tsx create mode 100644 integration/apps/next-app-turbopack/next-env.d.ts create mode 100644 integration/apps/next-app-turbopack/next.config.mjs create mode 100644 integration/apps/next-app-turbopack/package.json create mode 100644 integration/apps/next-app-turbopack/src/sprite/.gitignore create mode 100644 integration/apps/next-app-turbopack/src/sprite/index.ts create mode 100644 integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/next-app-turbopack/tsconfig.json create mode 100644 integration/apps/next-app-webpack/app/layout.tsx create mode 100644 integration/apps/next-app-webpack/app/page.tsx create mode 100644 integration/apps/next-app-webpack/app/style.css create mode 100644 integration/apps/next-app-webpack/next-env.d.ts create mode 100644 integration/apps/next-app-webpack/next.config.mjs create mode 100644 integration/apps/next-app-webpack/package.json create mode 100644 integration/apps/next-app-webpack/src/sprite/.gitignore create mode 100644 integration/apps/next-app-webpack/src/sprite/index.ts create mode 100644 integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/next-app-webpack/tsconfig.json create mode 100644 integration/apps/next-pages-turbopack/next-env.d.ts create mode 100644 integration/apps/next-pages-turbopack/next.config.mjs create mode 100644 integration/apps/next-pages-turbopack/package.json create mode 100644 integration/apps/next-pages-turbopack/pages/_app.tsx create mode 100644 integration/apps/next-pages-turbopack/pages/index.tsx create mode 100644 integration/apps/next-pages-turbopack/pages/style.css create mode 100644 integration/apps/next-pages-turbopack/src/sprite/.gitignore create mode 100644 integration/apps/next-pages-turbopack/src/sprite/index.ts create mode 100644 integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/next-pages-turbopack/tsconfig.json create mode 100644 integration/apps/next-pages-webpack/next-env.d.ts create mode 100644 integration/apps/next-pages-webpack/next.config.mjs create mode 100644 integration/apps/next-pages-webpack/package.json create mode 100644 integration/apps/next-pages-webpack/pages/_app.tsx create mode 100644 integration/apps/next-pages-webpack/pages/index.tsx create mode 100644 integration/apps/next-pages-webpack/pages/style.css create mode 100644 integration/apps/next-pages-webpack/src/sprite/.gitignore create mode 100644 integration/apps/next-pages-webpack/src/sprite/index.ts create mode 100644 integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/next-pages-webpack/tsconfig.json create mode 100644 integration/apps/nuxt/app/app.vue create mode 100644 integration/apps/nuxt/nuxt.config.ts create mode 100644 integration/apps/nuxt/package.json create mode 100644 integration/apps/nuxt/svg-sprites.config.ts create mode 100644 integration/apps/react-vite/index.html create mode 100644 integration/apps/react-vite/package.json create mode 100644 integration/apps/react-vite/src/main.tsx create mode 100644 integration/apps/react-vite/src/sprite/.gitignore create mode 100644 integration/apps/react-vite/src/sprite/index.ts create mode 100644 integration/apps/react-vite/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/react-vite/src/style.css create mode 100644 integration/apps/react-vite/src/vite-env.d.ts create mode 100644 integration/apps/react-vite/tsconfig.json create mode 100644 integration/apps/react-vite/vite.config.ts create mode 100644 integration/apps/react-webpack/package.json create mode 100644 integration/apps/react-webpack/src/env.d.ts create mode 100644 integration/apps/react-webpack/src/main.tsx create mode 100644 integration/apps/react-webpack/src/sprite/.gitignore create mode 100644 integration/apps/react-webpack/src/sprite/index.ts create mode 100644 integration/apps/react-webpack/src/sprite/svg-sprite.config.ts create mode 100644 integration/apps/react-webpack/src/style.css create mode 100644 integration/apps/react-webpack/tsconfig.json create mode 100644 integration/apps/react-webpack/webpack.config.js create mode 100644 integration/apps/solid-vite/index.html create mode 100644 integration/apps/solid-vite/package.json create mode 100644 integration/apps/solid-vite/src/main.tsx create mode 100644 integration/apps/solid-vite/src/style.css create mode 100644 integration/apps/solid-vite/src/vite-env.d.ts create mode 100644 integration/apps/solid-vite/svg-sprites.config.ts create mode 100644 integration/apps/solid-vite/tsconfig.json create mode 100644 integration/apps/solid-vite/vite.config.ts create mode 100644 integration/apps/svelte-vite/index.html create mode 100644 integration/apps/svelte-vite/package.json create mode 100644 integration/apps/svelte-vite/src/App.svelte create mode 100644 integration/apps/svelte-vite/src/app.d.ts create mode 100644 integration/apps/svelte-vite/src/main.ts create mode 100644 integration/apps/svelte-vite/src/vite-env.d.ts create mode 100644 integration/apps/svelte-vite/svg-sprites.config.ts create mode 100644 integration/apps/svelte-vite/tsconfig.json create mode 100644 integration/apps/svelte-vite/vite.config.ts create mode 100644 integration/apps/sveltekit/package.json create mode 100644 integration/apps/sveltekit/src/app.html create mode 100644 integration/apps/sveltekit/src/routes/+page.svelte create mode 100644 integration/apps/sveltekit/svelte.config.js create mode 100644 integration/apps/sveltekit/svg-sprites.config.ts create mode 100644 integration/apps/sveltekit/tsconfig.json create mode 100644 integration/apps/sveltekit/vite.config.ts create mode 100644 integration/apps/vanilla-vite/index.html create mode 100644 integration/apps/vanilla-vite/package.json create mode 100644 integration/apps/vanilla-vite/src/main.ts create mode 100644 integration/apps/vanilla-vite/src/style.css create mode 100644 integration/apps/vanilla-vite/src/vite-env.d.ts create mode 100644 integration/apps/vanilla-vite/svg-sprites.config.ts create mode 100644 integration/apps/vanilla-vite/tsconfig.json create mode 100644 integration/apps/vue-vite/index.html create mode 100644 integration/apps/vue-vite/package.json create mode 100644 integration/apps/vue-vite/src/App.vue create mode 100644 integration/apps/vue-vite/src/main.ts create mode 100644 integration/apps/vue-vite/src/vite-env.d.ts create mode 100644 integration/apps/vue-vite/svg-sprites.config.ts create mode 100644 integration/apps/vue-vite/tsconfig.json create mode 100644 integration/apps/vue-vite/vite.config.ts create mode 100644 integration/fixtures/icons/check.svg create mode 100644 integration/fixtures/icons/duotone.svg create mode 100644 integration/package-lock.json create mode 100644 integration/package.json create mode 100644 integration/playwright.config.ts create mode 100644 integration/scripts/apps.mjs create mode 100644 integration/scripts/run-workspaces.mjs create mode 100644 integration/scripts/serve-static.mjs create mode 100644 integration/tests/apps.spec.ts create mode 100644 integration/types/pngjs.d.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 943b597..9cda733 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,3 +40,36 @@ jobs: - name: Build run: npm run build + + integration: + name: Integration playground + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + cache-dependency-path: | + package-lock.json + integration/package-lock.json + + - name: Install package dependencies + run: npm ci + + - name: Build package + run: npm run build:package + + - name: Install playground dependencies + run: npm ci --prefix integration + + - name: Install Chromium + run: npm exec --prefix integration -- playwright install --with-deps chromium + + - name: Verify playground + run: npm run verify --prefix integration diff --git a/integration/.gitignore b/integration/.gitignore new file mode 100644 index 0000000..dd2ba75 --- /dev/null +++ b/integration/.gitignore @@ -0,0 +1,16 @@ +node_modules/ +playwright-report/ +test-results/ +apps/*/dist/ +apps/*/.angular/ +apps/*/.astro/ +apps/*/.next/ +apps/*/.nuxt/ +apps/*/.output/ +apps/*/.svelte-kit/ +apps/*/build/ +apps/*/out-tsc/ +apps/*/public/sprites/ +apps/*/static/sprites/ +apps/*/*.tsbuildinfo +apps/*/src/sprite/.svg-sprite/ diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 0000000..8471f93 --- /dev/null +++ b/integration/README.md @@ -0,0 +1,67 @@ +# Integration playground + +Постоянные минимальные consumer-приложения для проверки генерации, typecheck, +production build и отображения внешнего SVG-спрайта в настоящем Chromium. + +## Матрица + +| Fixture | Генератор | Runtime | +| --- | --- | --- | +| `react-vite` | `react@vite` | React + Vite | +| `react-webpack` | `react@webpack` | React + Webpack 5 | +| `next-app-turbopack` | `next@app/turbopack` | App Router + Turbopack | +| `next-app-webpack` | `next@app/webpack` | App Router + Webpack | +| `next-pages-turbopack` | `next@pages/turbopack` | Pages Router + Turbopack | +| `next-pages-webpack` | `next@pages/webpack` | Pages Router + Webpack | + +В verify-матрицу входят только поддерживаемые exact modes. Fixtures остальных +frameworks сохранены как заготовки и будут подключаться после появления их adapters. + +## Первый запуск + +Из корня репозитория: + +```bash +npm ci +npm run build:package +npm run integration:install +npm exec --prefix integration -- playwright install chromium +npm run integration:verify +``` + +`integration:verify` последовательно выполняет генерацию, typecheck, production build +активных fixtures и Playwright smoke-тесты. + +## Отдельные этапы + +```bash +npm run integration:generate +npm run integration:build +npm run integration:test +``` + +Для запуска команды только в одном fixture: + +```bash +node integration/scripts/run-workspaces.mjs build react-vite +npm run dev --workspace @svg-sprites-fixtures/react-vite --prefix integration +``` + +Перед E2E production builds должны существовать. Тест запускает каждый server +последовательно, проверяет внешний `href`, HTTP status и Content-Type спрайта, +наличие symbol ID, отсутствие browser errors и зелёные пиксели отрисованной иконки. + +## Структура + +```text +integration/ +├── apps/ # реальные consumer package boundaries +├── fixtures/icons/ # общие исходные SVG +├── scripts/ # workspace runner и static production server +├── tests/ # Playwright runtime smoke +├── package.json # отдельный npm workspace +└── package-lock.json # зафиксированная framework matrix +``` + +Generated-файлы, framework build outputs и Playwright artifacts исключены через +`integration/.gitignore`. diff --git a/integration/apps/angular/angular.json b/integration/apps/angular/angular.json new file mode 100644 index 0000000..9aeba5a --- /dev/null +++ b/integration/apps/angular/angular.json @@ -0,0 +1,53 @@ +{ + "$schema": "../../node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "angular": { + "projectType": "application", + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "index": "src/index.html", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": ["src/styles.css"], + "outputPath": "dist/angular" + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "angular:build:production" + }, + "development": { + "buildTarget": "angular:build:development" + } + }, + "defaultConfiguration": "development" + } + } + } + } +} diff --git a/integration/apps/angular/package.json b/integration/apps/angular/package.json new file mode 100644 index 0000000..0a70727 --- /dev/null +++ b/integration/apps/angular/package.json @@ -0,0 +1,27 @@ +{ + "name": "@svg-sprites-fixtures/angular", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "ngc -p tsconfig.app.json --noEmit", + "build": "npm run sprites && ng build --configuration production", + "dev": "npm run sprites && ng serve" + }, + "dependencies": { + "@angular/common": "21.2.18", + "@angular/compiler": "21.2.18", + "@angular/core": "21.2.18", + "@angular/platform-browser": "21.2.18", + "rxjs": "7.8.2", + "tslib": "2.8.1", + "zone.js": "0.16.2" + }, + "devDependencies": { + "@angular/build": "21.2.18", + "@angular/cli": "21.2.18", + "@angular/compiler-cli": "21.2.18", + "@gromlab/svg-sprites": "file:../../..", + "typescript": "5.9.3" + } +} diff --git a/integration/apps/angular/src/app.component.ts b/integration/apps/angular/src/app.component.ts new file mode 100644 index 0000000..e10b360 --- /dev/null +++ b/integration/apps/angular/src/app.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core' + +@Component({ + selector: 'app-root', + standalone: true, + template: ` +
+

Angular

+ + + +
+ `, +}) +export class AppComponent {} diff --git a/integration/apps/angular/src/index.html b/integration/apps/angular/src/index.html new file mode 100644 index 0000000..b059d27 --- /dev/null +++ b/integration/apps/angular/src/index.html @@ -0,0 +1,11 @@ + + + + + + Angular sprite fixture + + + + + diff --git a/integration/apps/angular/src/main.ts b/integration/apps/angular/src/main.ts new file mode 100644 index 0000000..f8432cf --- /dev/null +++ b/integration/apps/angular/src/main.ts @@ -0,0 +1,5 @@ +import { bootstrapApplication } from '@angular/platform-browser' + +import { AppComponent } from './app.component' + +bootstrapApplication(AppComponent).catch((error: unknown) => console.error(error)) diff --git a/integration/apps/angular/src/styles.css b/integration/apps/angular/src/styles.css new file mode 100644 index 0000000..55d4dfd --- /dev/null +++ b/integration/apps/angular/src/styles.css @@ -0,0 +1,16 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +[data-testid='icon'] { + width: 64px; + height: 64px; + color: #16a34a; +} diff --git a/integration/apps/angular/svg-sprites.config.ts b/integration/apps/angular/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/angular/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/angular/tsconfig.app.json b/integration/apps/angular/tsconfig.app.json new file mode 100644 index 0000000..5b9d3c5 --- /dev/null +++ b/integration/apps/angular/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/integration/apps/angular/tsconfig.json b/integration/apps/angular/tsconfig.json new file mode 100644 index 0000000..46004da --- /dev/null +++ b/integration/apps/angular/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022", "DOM"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/integration/apps/astro/package.json b/integration/apps/astro/package.json new file mode 100644 index 0000000..eb21793 --- /dev/null +++ b/integration/apps/astro/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/astro", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "astro check", + "build": "npm run sprites && astro check && astro build", + "dev": "npm run sprites && astro dev" + }, + "dependencies": { + "astro": "7.0.7" + }, + "devDependencies": { + "@astrojs/check": "0.9.9", + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2" + } +} diff --git a/integration/apps/astro/src/pages/index.astro b/integration/apps/astro/src/pages/index.astro new file mode 100644 index 0000000..a5b9b4b --- /dev/null +++ b/integration/apps/astro/src/pages/index.astro @@ -0,0 +1,44 @@ +--- +const title = 'Astro sprite fixture' +--- + + + + + + + {title} + + +
+

Astro

+ + + +
+ + + + diff --git a/integration/apps/astro/svg-sprites.config.ts b/integration/apps/astro/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/astro/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/astro/tsconfig.json b/integration/apps/astro/tsconfig.json new file mode 100644 index 0000000..bcbf8b5 --- /dev/null +++ b/integration/apps/astro/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} diff --git a/integration/apps/next-app-turbopack/app/layout.tsx b/integration/apps/next-app-turbopack/app/layout.tsx new file mode 100644 index 0000000..6951b8c --- /dev/null +++ b/integration/apps/next-app-turbopack/app/layout.tsx @@ -0,0 +1,11 @@ +import type { ReactNode } from 'react' + +import './style.css' + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/integration/apps/next-app-turbopack/app/page.tsx b/integration/apps/next-app-turbopack/app/page.tsx new file mode 100644 index 0000000..b72c26d --- /dev/null +++ b/integration/apps/next-app-turbopack/app/page.tsx @@ -0,0 +1,18 @@ +import { IconsIcon } from '../src/sprite' + +export default function Page() { + return ( +
+

Next.js App Router + Turbopack

+ +
+ ) +} diff --git a/integration/apps/next-app-turbopack/app/style.css b/integration/apps/next-app-turbopack/app/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/next-app-turbopack/app/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/next-app-turbopack/generated-contract.typecheck.tsx b/integration/apps/next-app-turbopack/generated-contract.typecheck.tsx new file mode 100644 index 0000000..1b8ac4e --- /dev/null +++ b/integration/apps/next-app-turbopack/generated-contract.typecheck.tsx @@ -0,0 +1,18 @@ +import { IconsIcon, iconsIconNames } from './src/sprite' +import type { IconsIconName, IconsIconProps, IconsIconStyle } from './src/sprite' +import { IconsIcon as GeneratedIconsIcon } from './src/sprite/.svg-sprite/react/react-component.js' +import type { IconsIconName as GeneratedIconName } from './src/sprite/.svg-sprite/icon-data.js' +import { iconsIconNames as generatedIconNames } from './src/sprite/.svg-sprite/icon-data.js' + +const iconName: IconsIconName = iconsIconNames[0] +const generatedIconName: GeneratedIconName = generatedIconNames[0] +const allIconNames: readonly IconsIconName[] = iconsIconNames +const style: IconsIconStyle = { '--icon-color-1': '#16a34a' } +const props: IconsIconProps = { icon: iconName, style } + +void allIconNames +void +void + +// @ts-expect-error Unknown icon names must be rejected by generated declarations. +void diff --git a/integration/apps/next-app-turbopack/next-env.d.ts b/integration/apps/next-app-turbopack/next-env.d.ts new file mode 100644 index 0000000..9edff1c --- /dev/null +++ b/integration/apps/next-app-turbopack/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/integration/apps/next-app-turbopack/next.config.mjs b/integration/apps/next-app-turbopack/next.config.mjs new file mode 100644 index 0000000..fe967c5 --- /dev/null +++ b/integration/apps/next-app-turbopack/next.config.mjs @@ -0,0 +1,10 @@ +import path from 'node:path' + +const integrationRoot = path.resolve(import.meta.dirname, '../..') + +export default { + outputFileTracingRoot: integrationRoot, + turbopack: { + root: integrationRoot, + }, +} diff --git a/integration/apps/next-app-turbopack/package.json b/integration/apps/next-app-turbopack/package.json new file mode 100644 index 0000000..7988150 --- /dev/null +++ b/integration/apps/next-app-turbopack/package.json @@ -0,0 +1,24 @@ +{ + "name": "@svg-sprites-fixtures/next-app-turbopack", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && next build --turbopack", + "dev": "npm run sprites && next dev --turbopack", + "start": "next start" + }, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } +} diff --git a/integration/apps/next-app-turbopack/src/sprite/.gitignore b/integration/apps/next-app-turbopack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/next-app-turbopack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/next-app-turbopack/src/sprite/index.ts b/integration/apps/next-app-turbopack/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/next-app-turbopack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts b/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..985b455 --- /dev/null +++ b/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/next-app-turbopack/tsconfig.json b/integration/apps/next-app-turbopack/tsconfig.json new file mode 100644 index 0000000..3ebc65b --- /dev/null +++ b/integration/apps/next-app-turbopack/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], + "incremental": true + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/integration/apps/next-app-webpack/app/layout.tsx b/integration/apps/next-app-webpack/app/layout.tsx new file mode 100644 index 0000000..6951b8c --- /dev/null +++ b/integration/apps/next-app-webpack/app/layout.tsx @@ -0,0 +1,11 @@ +import type { ReactNode } from 'react' + +import './style.css' + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/integration/apps/next-app-webpack/app/page.tsx b/integration/apps/next-app-webpack/app/page.tsx new file mode 100644 index 0000000..e4a92a3 --- /dev/null +++ b/integration/apps/next-app-webpack/app/page.tsx @@ -0,0 +1,18 @@ +import { IconsIcon } from '../src/sprite' + +export default function Page() { + return ( +
+

Next.js App Router + Webpack

+ +
+ ) +} diff --git a/integration/apps/next-app-webpack/app/style.css b/integration/apps/next-app-webpack/app/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/next-app-webpack/app/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/next-app-webpack/next-env.d.ts b/integration/apps/next-app-webpack/next-env.d.ts new file mode 100644 index 0000000..9edff1c --- /dev/null +++ b/integration/apps/next-app-webpack/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/integration/apps/next-app-webpack/next.config.mjs b/integration/apps/next-app-webpack/next.config.mjs new file mode 100644 index 0000000..fe967c5 --- /dev/null +++ b/integration/apps/next-app-webpack/next.config.mjs @@ -0,0 +1,10 @@ +import path from 'node:path' + +const integrationRoot = path.resolve(import.meta.dirname, '../..') + +export default { + outputFileTracingRoot: integrationRoot, + turbopack: { + root: integrationRoot, + }, +} diff --git a/integration/apps/next-app-webpack/package.json b/integration/apps/next-app-webpack/package.json new file mode 100644 index 0000000..746de2f --- /dev/null +++ b/integration/apps/next-app-webpack/package.json @@ -0,0 +1,24 @@ +{ + "name": "@svg-sprites-fixtures/next-app-webpack", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && next build --webpack", + "dev": "npm run sprites && next dev --webpack", + "start": "next start" + }, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } +} diff --git a/integration/apps/next-app-webpack/src/sprite/.gitignore b/integration/apps/next-app-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/next-app-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/next-app-webpack/src/sprite/index.ts b/integration/apps/next-app-webpack/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/next-app-webpack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..35850ae --- /dev/null +++ b/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/webpack', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/next-app-webpack/tsconfig.json b/integration/apps/next-app-webpack/tsconfig.json new file mode 100644 index 0000000..3ebc65b --- /dev/null +++ b/integration/apps/next-app-webpack/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], + "incremental": true + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/integration/apps/next-pages-turbopack/next-env.d.ts b/integration/apps/next-pages-turbopack/next-env.d.ts new file mode 100644 index 0000000..1970904 --- /dev/null +++ b/integration/apps/next-pages-turbopack/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/integration/apps/next-pages-turbopack/next.config.mjs b/integration/apps/next-pages-turbopack/next.config.mjs new file mode 100644 index 0000000..fe967c5 --- /dev/null +++ b/integration/apps/next-pages-turbopack/next.config.mjs @@ -0,0 +1,10 @@ +import path from 'node:path' + +const integrationRoot = path.resolve(import.meta.dirname, '../..') + +export default { + outputFileTracingRoot: integrationRoot, + turbopack: { + root: integrationRoot, + }, +} diff --git a/integration/apps/next-pages-turbopack/package.json b/integration/apps/next-pages-turbopack/package.json new file mode 100644 index 0000000..00c7b3f --- /dev/null +++ b/integration/apps/next-pages-turbopack/package.json @@ -0,0 +1,24 @@ +{ + "name": "@svg-sprites-fixtures/next-pages-turbopack", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && next build --turbopack", + "dev": "npm run sprites && next dev --turbopack", + "start": "next start" + }, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } +} diff --git a/integration/apps/next-pages-turbopack/pages/_app.tsx b/integration/apps/next-pages-turbopack/pages/_app.tsx new file mode 100644 index 0000000..9fef17b --- /dev/null +++ b/integration/apps/next-pages-turbopack/pages/_app.tsx @@ -0,0 +1,7 @@ +import type { AppProps } from 'next/app' + +import './style.css' + +export default function App({ Component, pageProps }: AppProps) { + return +} diff --git a/integration/apps/next-pages-turbopack/pages/index.tsx b/integration/apps/next-pages-turbopack/pages/index.tsx new file mode 100644 index 0000000..c3e3162 --- /dev/null +++ b/integration/apps/next-pages-turbopack/pages/index.tsx @@ -0,0 +1,18 @@ +import { IconsIcon } from '../src/sprite' + +export default function Page() { + return ( +
+

Next.js Pages Router + Turbopack

+ +
+ ) +} diff --git a/integration/apps/next-pages-turbopack/pages/style.css b/integration/apps/next-pages-turbopack/pages/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/next-pages-turbopack/pages/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/next-pages-turbopack/src/sprite/.gitignore b/integration/apps/next-pages-turbopack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/next-pages-turbopack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/next-pages-turbopack/src/sprite/index.ts b/integration/apps/next-pages-turbopack/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/next-pages-turbopack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts b/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..c26c99d --- /dev/null +++ b/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/turbopack', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/next-pages-turbopack/tsconfig.json b/integration/apps/next-pages-turbopack/tsconfig.json new file mode 100644 index 0000000..c4b2ab3 --- /dev/null +++ b/integration/apps/next-pages-turbopack/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], + "incremental": true + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/integration/apps/next-pages-webpack/next-env.d.ts b/integration/apps/next-pages-webpack/next-env.d.ts new file mode 100644 index 0000000..1970904 --- /dev/null +++ b/integration/apps/next-pages-webpack/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/integration/apps/next-pages-webpack/next.config.mjs b/integration/apps/next-pages-webpack/next.config.mjs new file mode 100644 index 0000000..fe967c5 --- /dev/null +++ b/integration/apps/next-pages-webpack/next.config.mjs @@ -0,0 +1,10 @@ +import path from 'node:path' + +const integrationRoot = path.resolve(import.meta.dirname, '../..') + +export default { + outputFileTracingRoot: integrationRoot, + turbopack: { + root: integrationRoot, + }, +} diff --git a/integration/apps/next-pages-webpack/package.json b/integration/apps/next-pages-webpack/package.json new file mode 100644 index 0000000..0055a29 --- /dev/null +++ b/integration/apps/next-pages-webpack/package.json @@ -0,0 +1,24 @@ +{ + "name": "@svg-sprites-fixtures/next-pages-webpack", + "private": true, + "version": "0.0.0", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && next build --webpack", + "dev": "npm run sprites && next dev --webpack", + "start": "next start" + }, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } +} diff --git a/integration/apps/next-pages-webpack/pages/_app.tsx b/integration/apps/next-pages-webpack/pages/_app.tsx new file mode 100644 index 0000000..9fef17b --- /dev/null +++ b/integration/apps/next-pages-webpack/pages/_app.tsx @@ -0,0 +1,7 @@ +import type { AppProps } from 'next/app' + +import './style.css' + +export default function App({ Component, pageProps }: AppProps) { + return +} diff --git a/integration/apps/next-pages-webpack/pages/index.tsx b/integration/apps/next-pages-webpack/pages/index.tsx new file mode 100644 index 0000000..494d220 --- /dev/null +++ b/integration/apps/next-pages-webpack/pages/index.tsx @@ -0,0 +1,18 @@ +import { IconsIcon } from '../src/sprite' + +export default function Page() { + return ( +
+

Next.js Pages Router + Webpack

+ +
+ ) +} diff --git a/integration/apps/next-pages-webpack/pages/style.css b/integration/apps/next-pages-webpack/pages/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/next-pages-webpack/pages/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/next-pages-webpack/src/sprite/.gitignore b/integration/apps/next-pages-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/next-pages-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/next-pages-webpack/src/sprite/index.ts b/integration/apps/next-pages-webpack/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/next-pages-webpack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..f059336 --- /dev/null +++ b/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/webpack', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/next-pages-webpack/tsconfig.json b/integration/apps/next-pages-webpack/tsconfig.json new file mode 100644 index 0000000..c4b2ab3 --- /dev/null +++ b/integration/apps/next-pages-webpack/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], + "incremental": true + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/integration/apps/nuxt/app/app.vue b/integration/apps/nuxt/app/app.vue new file mode 100644 index 0000000..46f4062 --- /dev/null +++ b/integration/apps/nuxt/app/app.vue @@ -0,0 +1,32 @@ + + + diff --git a/integration/apps/nuxt/nuxt.config.ts b/integration/apps/nuxt/nuxt.config.ts new file mode 100644 index 0000000..259b4b0 --- /dev/null +++ b/integration/apps/nuxt/nuxt.config.ts @@ -0,0 +1,4 @@ +export default defineNuxtConfig({ + compatibilityDate: '2026-07-01', + devtools: { enabled: false }, +}) diff --git a/integration/apps/nuxt/package.json b/integration/apps/nuxt/package.json new file mode 100644 index 0000000..fabbae5 --- /dev/null +++ b/integration/apps/nuxt/package.json @@ -0,0 +1,22 @@ +{ + "name": "@svg-sprites-fixtures/nuxt", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "nuxt typecheck", + "build": "npm run sprites && nuxt build", + "dev": "npm run sprites && nuxt dev", + "start": "node .output/server/index.mjs" + }, + "dependencies": { + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } +} diff --git a/integration/apps/nuxt/svg-sprites.config.ts b/integration/apps/nuxt/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/nuxt/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/react-vite/index.html b/integration/apps/react-vite/index.html new file mode 100644 index 0000000..368f850 --- /dev/null +++ b/integration/apps/react-vite/index.html @@ -0,0 +1,12 @@ + + + + + + React Vite sprite fixture + + +
+ + + diff --git a/integration/apps/react-vite/package.json b/integration/apps/react-vite/package.json new file mode 100644 index 0000000..ac3d717 --- /dev/null +++ b/integration/apps/react-vite/package.json @@ -0,0 +1,24 @@ +{ + "name": "@svg-sprites-fixtures/react-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && tsc --noEmit && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@vitejs/plugin-react": "6.0.1", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/react-vite/src/main.tsx b/integration/apps/react-vite/src/main.tsx new file mode 100644 index 0000000..53d19dd --- /dev/null +++ b/integration/apps/react-vite/src/main.tsx @@ -0,0 +1,22 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' + +import { IconsIcon } from './sprite' +import './style.css' + +createRoot(document.getElementById('root')!).render( + +
+

React + Vite

+ +
+
, +) diff --git a/integration/apps/react-vite/src/sprite/.gitignore b/integration/apps/react-vite/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/react-vite/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/react-vite/src/sprite/index.ts b/integration/apps/react-vite/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/react-vite/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/react-vite/src/sprite/svg-sprite.config.ts b/integration/apps/react-vite/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..fb603c4 --- /dev/null +++ b/integration/apps/react-vite/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/react-vite/src/style.css b/integration/apps/react-vite/src/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/react-vite/src/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/react-vite/src/vite-env.d.ts b/integration/apps/react-vite/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/integration/apps/react-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration/apps/react-vite/tsconfig.json b/integration/apps/react-vite/tsconfig.json new file mode 100644 index 0000000..191305a --- /dev/null +++ b/integration/apps/react-vite/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "react-jsx", + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/integration/apps/react-vite/vite.config.ts b/integration/apps/react-vite/vite.config.ts new file mode 100644 index 0000000..ae74518 --- /dev/null +++ b/integration/apps/react-vite/vite.config.ts @@ -0,0 +1,6 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [react()], +}) diff --git a/integration/apps/react-webpack/package.json b/integration/apps/react-webpack/package.json new file mode 100644 index 0000000..dc85bd3 --- /dev/null +++ b/integration/apps/react-webpack/package.json @@ -0,0 +1,29 @@ +{ + "name": "@svg-sprites-fixtures/react-webpack", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites src/sprite/svg-sprite.config.ts", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && webpack --mode production", + "dev": "npm run sprites && webpack serve --mode development" + }, + "dependencies": { + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "ts-loader": "9.6.2", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } +} diff --git a/integration/apps/react-webpack/src/env.d.ts b/integration/apps/react-webpack/src/env.d.ts new file mode 100644 index 0000000..bc5e482 --- /dev/null +++ b/integration/apps/react-webpack/src/env.d.ts @@ -0,0 +1,6 @@ +declare module '*.module.css' { + const classes: Record + export default classes +} + +declare module '*.css' diff --git a/integration/apps/react-webpack/src/main.tsx b/integration/apps/react-webpack/src/main.tsx new file mode 100644 index 0000000..df02e60 --- /dev/null +++ b/integration/apps/react-webpack/src/main.tsx @@ -0,0 +1,22 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' + +import { IconsIcon } from './sprite' +import './style.css' + +createRoot(document.getElementById('root')!).render( + +
+

React + Webpack

+ +
+
, +) diff --git a/integration/apps/react-webpack/src/sprite/.gitignore b/integration/apps/react-webpack/src/sprite/.gitignore new file mode 100644 index 0000000..7f3344e --- /dev/null +++ b/integration/apps/react-webpack/src/sprite/.gitignore @@ -0,0 +1,2 @@ +# @generated by @gromlab/svg-sprites. Do not edit. +/.svg-sprite/ diff --git a/integration/apps/react-webpack/src/sprite/index.ts b/integration/apps/react-webpack/src/sprite/index.ts new file mode 100644 index 0000000..411538d --- /dev/null +++ b/integration/apps/react-webpack/src/sprite/index.ts @@ -0,0 +1 @@ +export * from './.svg-sprite' diff --git a/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts new file mode 100644 index 0000000..9043d84 --- /dev/null +++ b/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts @@ -0,0 +1,8 @@ +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@webpack', + name: 'icons', + inputFiles: ['../../../../fixtures/icons/check.svg'], + generatedNotice: false, +}) diff --git a/integration/apps/react-webpack/src/style.css b/integration/apps/react-webpack/src/style.css new file mode 100644 index 0000000..b140fd0 --- /dev/null +++ b/integration/apps/react-webpack/src/style.css @@ -0,0 +1,10 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} diff --git a/integration/apps/react-webpack/tsconfig.json b/integration/apps/react-webpack/tsconfig.json new file mode 100644 index 0000000..4d4bdb9 --- /dev/null +++ b/integration/apps/react-webpack/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/integration/apps/react-webpack/webpack.config.js b/integration/apps/react-webpack/webpack.config.js new file mode 100644 index 0000000..0dd921a --- /dev/null +++ b/integration/apps/react-webpack/webpack.config.js @@ -0,0 +1,52 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import HtmlWebpackPlugin from 'html-webpack-plugin' + +const root = path.dirname(fileURLToPath(import.meta.url)) + +export default { + entry: './src/main.tsx', + output: { + path: path.join(root, 'dist'), + filename: 'assets/app.[contenthash].js', + assetModuleFilename: 'assets/[name].[contenthash][ext]', + clean: true, + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: 'ts-loader', + }, + { + test: /\.module\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: { + namedExport: false, + }, + }, + }, + ], + }, + { + test: /\.css$/, + exclude: /\.module\.css$/, + use: ['style-loader', 'css-loader'], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + templateContent: 'React Webpack sprite fixture
', + }), + ], +} diff --git a/integration/apps/solid-vite/index.html b/integration/apps/solid-vite/index.html new file mode 100644 index 0000000..9c1ee0f --- /dev/null +++ b/integration/apps/solid-vite/index.html @@ -0,0 +1,12 @@ + + + + + + Solid Vite sprite fixture + + +
+ + + diff --git a/integration/apps/solid-vite/package.json b/integration/apps/solid-vite/package.json new file mode 100644 index 0000000..8f6650d --- /dev/null +++ b/integration/apps/solid-vite/package.json @@ -0,0 +1,21 @@ +{ + "name": "@svg-sprites-fixtures/solid-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && tsc --noEmit && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "solid-js": "1.9.14" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4", + "vite-plugin-solid": "2.11.12" + } +} diff --git a/integration/apps/solid-vite/src/main.tsx b/integration/apps/solid-vite/src/main.tsx new file mode 100644 index 0000000..d15a93c --- /dev/null +++ b/integration/apps/solid-vite/src/main.tsx @@ -0,0 +1,21 @@ +import { render } from 'solid-js/web' + +import './style.css' + +function App() { + return ( +
+

Solid + Vite

+ + + +
+ ) +} + +render(() => , document.getElementById('root')!) diff --git a/integration/apps/solid-vite/src/style.css b/integration/apps/solid-vite/src/style.css new file mode 100644 index 0000000..55d4dfd --- /dev/null +++ b/integration/apps/solid-vite/src/style.css @@ -0,0 +1,16 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +[data-testid='icon'] { + width: 64px; + height: 64px; + color: #16a34a; +} diff --git a/integration/apps/solid-vite/src/vite-env.d.ts b/integration/apps/solid-vite/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/integration/apps/solid-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration/apps/solid-vite/svg-sprites.config.ts b/integration/apps/solid-vite/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/solid-vite/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/solid-vite/tsconfig.json b/integration/apps/solid-vite/tsconfig.json new file mode 100644 index 0000000..fcce3f7 --- /dev/null +++ b/integration/apps/solid-vite/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "preserve", + "jsxImportSource": "solid-js", + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/integration/apps/solid-vite/vite.config.ts b/integration/apps/solid-vite/vite.config.ts new file mode 100644 index 0000000..4095d9b --- /dev/null +++ b/integration/apps/solid-vite/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import solid from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solid()], +}) diff --git a/integration/apps/svelte-vite/index.html b/integration/apps/svelte-vite/index.html new file mode 100644 index 0000000..049c910 --- /dev/null +++ b/integration/apps/svelte-vite/index.html @@ -0,0 +1,12 @@ + + + + + + Svelte Vite sprite fixture + + +
+ + + diff --git a/integration/apps/svelte-vite/package.json b/integration/apps/svelte-vite/package.json new file mode 100644 index 0000000..ed009b7 --- /dev/null +++ b/integration/apps/svelte-vite/package.json @@ -0,0 +1,22 @@ +{ + "name": "@svg-sprites-fixtures/svelte-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "svelte-check --tsconfig ./tsconfig.json", + "build": "npm run sprites && svelte-check --tsconfig ./tsconfig.json && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte-check": "4.7.2", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/svelte-vite/src/App.svelte b/integration/apps/svelte-vite/src/App.svelte new file mode 100644 index 0000000..1c6c801 --- /dev/null +++ b/integration/apps/svelte-vite/src/App.svelte @@ -0,0 +1,30 @@ +
+

Svelte + Vite

+ + + +
+ + diff --git a/integration/apps/svelte-vite/src/app.d.ts b/integration/apps/svelte-vite/src/app.d.ts new file mode 100644 index 0000000..fc8b85a --- /dev/null +++ b/integration/apps/svelte-vite/src/app.d.ts @@ -0,0 +1,6 @@ +declare module '*.svelte' { + import type { Component } from 'svelte' + + const component: Component> + export default component +} diff --git a/integration/apps/svelte-vite/src/main.ts b/integration/apps/svelte-vite/src/main.ts new file mode 100644 index 0000000..5307270 --- /dev/null +++ b/integration/apps/svelte-vite/src/main.ts @@ -0,0 +1,7 @@ +import { mount } from 'svelte' + +import App from './App.svelte' + +mount(App, { + target: document.getElementById('app')!, +}) diff --git a/integration/apps/svelte-vite/src/vite-env.d.ts b/integration/apps/svelte-vite/src/vite-env.d.ts new file mode 100644 index 0000000..308d857 --- /dev/null +++ b/integration/apps/svelte-vite/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/integration/apps/svelte-vite/svg-sprites.config.ts b/integration/apps/svelte-vite/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/svelte-vite/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/svelte-vite/tsconfig.json b/integration/apps/svelte-vite/tsconfig.json new file mode 100644 index 0000000..1db664c --- /dev/null +++ b/integration/apps/svelte-vite/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowArbitraryExtensions": true, + "isolatedModules": true, + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts", "src/**/*.svelte"] +} diff --git a/integration/apps/svelte-vite/vite.config.ts b/integration/apps/svelte-vite/vite.config.ts new file mode 100644 index 0000000..8ad4e19 --- /dev/null +++ b/integration/apps/svelte-vite/vite.config.ts @@ -0,0 +1,6 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [svelte()], +}) diff --git a/integration/apps/sveltekit/package.json b/integration/apps/sveltekit/package.json new file mode 100644 index 0000000..7d10104 --- /dev/null +++ b/integration/apps/sveltekit/package.json @@ -0,0 +1,25 @@ +{ + "name": "@svg-sprites-fixtures/sveltekit", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "build": "npm run sprites && svelte-kit sync && vite build", + "dev": "npm run sprites && vite dev", + "start": "node build/index.js" + }, + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@sveltejs/adapter-node": "5.5.7", + "@sveltejs/kit": "2.69.2", + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte-check": "4.7.2", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/sveltekit/src/app.html b/integration/apps/sveltekit/src/app.html new file mode 100644 index 0000000..adf8bd8 --- /dev/null +++ b/integration/apps/sveltekit/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/integration/apps/sveltekit/src/routes/+page.svelte b/integration/apps/sveltekit/src/routes/+page.svelte new file mode 100644 index 0000000..4da36fb --- /dev/null +++ b/integration/apps/sveltekit/src/routes/+page.svelte @@ -0,0 +1,34 @@ + + SvelteKit sprite fixture + + +
+

SvelteKit

+ + + +
+ + diff --git a/integration/apps/sveltekit/svelte.config.js b/integration/apps/sveltekit/svelte.config.js new file mode 100644 index 0000000..c352c86 --- /dev/null +++ b/integration/apps/sveltekit/svelte.config.js @@ -0,0 +1,7 @@ +import adapter from '@sveltejs/adapter-node' + +export default { + kit: { + adapter: adapter(), + }, +} diff --git a/integration/apps/sveltekit/svg-sprites.config.ts b/integration/apps/sveltekit/svg-sprites.config.ts new file mode 100644 index 0000000..dc80240 --- /dev/null +++ b/integration/apps/sveltekit/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'static/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/sveltekit/tsconfig.json b/integration/apps/sveltekit/tsconfig.json new file mode 100644 index 0000000..d90eeec --- /dev/null +++ b/integration/apps/sveltekit/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "Bundler" + } +} diff --git a/integration/apps/sveltekit/vite.config.ts b/integration/apps/sveltekit/vite.config.ts new file mode 100644 index 0000000..dd1b8b7 --- /dev/null +++ b/integration/apps/sveltekit/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [sveltekit()], +}) diff --git a/integration/apps/vanilla-vite/index.html b/integration/apps/vanilla-vite/index.html new file mode 100644 index 0000000..b666344 --- /dev/null +++ b/integration/apps/vanilla-vite/index.html @@ -0,0 +1,12 @@ + + + + + + Vanilla Vite sprite fixture + + +
+ + + diff --git a/integration/apps/vanilla-vite/package.json b/integration/apps/vanilla-vite/package.json new file mode 100644 index 0000000..27c094f --- /dev/null +++ b/integration/apps/vanilla-vite/package.json @@ -0,0 +1,17 @@ +{ + "name": "@svg-sprites-fixtures/vanilla-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "tsc --noEmit", + "build": "npm run sprites && tsc --noEmit && vite build", + "dev": "npm run sprites && vite" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } +} diff --git a/integration/apps/vanilla-vite/src/main.ts b/integration/apps/vanilla-vite/src/main.ts new file mode 100644 index 0000000..e176370 --- /dev/null +++ b/integration/apps/vanilla-vite/src/main.ts @@ -0,0 +1,8 @@ +import './style.css' + +document.querySelector('#app')!.innerHTML = ` +

Vanilla + Vite

+ + + +` diff --git a/integration/apps/vanilla-vite/src/style.css b/integration/apps/vanilla-vite/src/style.css new file mode 100644 index 0000000..55d4dfd --- /dev/null +++ b/integration/apps/vanilla-vite/src/style.css @@ -0,0 +1,16 @@ +:root { + font-family: system-ui, sans-serif; + color: #172033; + background: #fff; +} + +body { + margin: 0; + padding: 40px; +} + +[data-testid='icon'] { + width: 64px; + height: 64px; + color: #16a34a; +} diff --git a/integration/apps/vanilla-vite/src/vite-env.d.ts b/integration/apps/vanilla-vite/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/integration/apps/vanilla-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration/apps/vanilla-vite/svg-sprites.config.ts b/integration/apps/vanilla-vite/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/vanilla-vite/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/vanilla-vite/tsconfig.json b/integration/apps/vanilla-vite/tsconfig.json new file mode 100644 index 0000000..b4519d0 --- /dev/null +++ b/integration/apps/vanilla-vite/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM"], + "strict": true, + "noEmit": true + }, + "include": ["src"] +} diff --git a/integration/apps/vue-vite/index.html b/integration/apps/vue-vite/index.html new file mode 100644 index 0000000..ac2f7be --- /dev/null +++ b/integration/apps/vue-vite/index.html @@ -0,0 +1,12 @@ + + + + + + Vue Vite sprite fixture + + +
+ + + diff --git a/integration/apps/vue-vite/package.json b/integration/apps/vue-vite/package.json new file mode 100644 index 0000000..59ea81c --- /dev/null +++ b/integration/apps/vue-vite/package.json @@ -0,0 +1,22 @@ +{ + "name": "@svg-sprites-fixtures/vue-vite", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "sprites": "svg-sprites --mode legacy .", + "typecheck": "vue-tsc --noEmit", + "build": "npm run sprites && vue-tsc --noEmit && vite build", + "dev": "npm run sprites && vite" + }, + "dependencies": { + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@vitejs/plugin-vue": "6.0.7", + "typescript": "6.0.2", + "vite": "8.1.4", + "vue-tsc": "3.3.7" + } +} diff --git a/integration/apps/vue-vite/src/App.vue b/integration/apps/vue-vite/src/App.vue new file mode 100644 index 0000000..eb2129e --- /dev/null +++ b/integration/apps/vue-vite/src/App.vue @@ -0,0 +1,32 @@ + + + diff --git a/integration/apps/vue-vite/src/main.ts b/integration/apps/vue-vite/src/main.ts new file mode 100644 index 0000000..912d54f --- /dev/null +++ b/integration/apps/vue-vite/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' + +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/integration/apps/vue-vite/src/vite-env.d.ts b/integration/apps/vue-vite/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/integration/apps/vue-vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration/apps/vue-vite/svg-sprites.config.ts b/integration/apps/vue-vite/svg-sprites.config.ts new file mode 100644 index 0000000..24da7e0 --- /dev/null +++ b/integration/apps/vue-vite/svg-sprites.config.ts @@ -0,0 +1,11 @@ +export default { + output: 'public/sprites', + preview: false, + sprites: [ + { + name: 'icons', + input: '../../fixtures/icons', + format: 'symbol', + }, + ], +} diff --git a/integration/apps/vue-vite/tsconfig.json b/integration/apps/vue-vite/tsconfig.json new file mode 100644 index 0000000..3830b59 --- /dev/null +++ b/integration/apps/vue-vite/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts", "src/**/*.vue"] +} diff --git a/integration/apps/vue-vite/vite.config.ts b/integration/apps/vue-vite/vite.config.ts new file mode 100644 index 0000000..544087a --- /dev/null +++ b/integration/apps/vue-vite/vite.config.ts @@ -0,0 +1,6 @@ +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [vue()], +}) diff --git a/integration/fixtures/icons/check.svg b/integration/fixtures/icons/check.svg new file mode 100644 index 0000000..d7c296a --- /dev/null +++ b/integration/fixtures/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/integration/fixtures/icons/duotone.svg b/integration/fixtures/icons/duotone.svg new file mode 100644 index 0000000..98962c1 --- /dev/null +++ b/integration/fixtures/icons/duotone.svg @@ -0,0 +1,4 @@ + + + + diff --git a/integration/package-lock.json b/integration/package-lock.json new file mode 100644 index 0000000..aae018b --- /dev/null +++ b/integration/package-lock.json @@ -0,0 +1,24458 @@ +{ + "name": "@svg-sprites-fixtures/integration", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@svg-sprites-fixtures/integration", + "version": "0.0.0", + "workspaces": [ + "apps/*" + ], + "devDependencies": { + "@playwright/test": "1.61.1", + "pngjs": "7.0.0" + } + }, + "..": { + "name": "@gromlab/svg-sprites", + "version": "1.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.20", + "jiti": "^2.6.1", + "svg-sprite": "^2.0.4" + }, + "bin": { + "svg-sprites": "dist/cli.js" + }, + "devDependencies": { + "@types/node": "^22.15.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/svg-sprite": "^0.0.39", + "jsdom": "^26.1.0", + "next": "16.2.10", + "react": "^19.2.5", + "react-colorful": "^5.6.1", + "react-dom": "^19.2.5", + "tsup": "^8.4.0", + "typescript": "^5.8.3", + "vite": "^6.4.3", + "webpack": "^5.108.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": ">=18 <20" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "apps/angular": { + "name": "@svg-sprites-fixtures/angular", + "version": "0.0.0", + "dependencies": { + "@angular/common": "21.2.18", + "@angular/compiler": "21.2.18", + "@angular/core": "21.2.18", + "@angular/platform-browser": "21.2.18", + "rxjs": "7.8.2", + "tslib": "2.8.1", + "zone.js": "0.16.2" + }, + "devDependencies": { + "@angular/build": "21.2.18", + "@angular/cli": "21.2.18", + "@angular/compiler-cli": "21.2.18", + "@gromlab/svg-sprites": "file:../../..", + "typescript": "5.9.3" + } + }, + "apps/angular/node_modules/@algolia/abtesting": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.1.tgz", + "integrity": "sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-abtesting": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.48.1.tgz", + "integrity": "sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-analytics": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.48.1.tgz", + "integrity": "sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-common": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.48.1.tgz", + "integrity": "sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-insights": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.48.1.tgz", + "integrity": "sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-personalization": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.48.1.tgz", + "integrity": "sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-query-suggestions": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.48.1.tgz", + "integrity": "sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/client-search": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.48.1.tgz", + "integrity": "sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/ingestion": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.48.1.tgz", + "integrity": "sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/monitoring": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.48.1.tgz", + "integrity": "sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/recommend": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.48.1.tgz", + "integrity": "sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/requester-browser-xhr": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.48.1.tgz", + "integrity": "sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/requester-fetch": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.48.1.tgz", + "integrity": "sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@algolia/requester-node-http": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.48.1.tgz", + "integrity": "sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/@angular-devkit/architect": { + "version": "0.2102.18", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.18.tgz", + "integrity": "sha512-bEkF4ApWa6FJ2+ZyFPdhQr05PgJv/oKwx+3goNRa4lSxzJO+O3KXr0ivTyE5zEJ8l4JdV3h93bsCP54Rk3v7Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.2.18", + "rxjs": "7.8.2" + }, + "bin": { + "architect": "bin/cli.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "apps/angular/node_modules/@angular-devkit/core": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.18.tgz", + "integrity": "sha512-783AuDmzAjFkOBHey7cx1mXd8CTiMg9LvG+BCxEp8i4EB7cOGYrq3lBzkwhKJq6yYpz7dNgM1QgeLk+NcrFBSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^5.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "apps/angular/node_modules/@angular-devkit/schematics": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.18.tgz", + "integrity": "sha512-PakHs+Eefx06KORDFyH4wmkIZ/wm4mI4DqhXe4XFuWJOazMQoYJxPmMeby47BD0HhvJN7BskN61CPS8HCAvICg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.2.18", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.21", + "ora": "9.3.0", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "apps/angular/node_modules/@angular/build": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.18.tgz", + "integrity": "sha512-9Jic0Dud6d0ULPR5SceHV86AzZE42cNWPsbLKrK1dUneaAC44GSVRTmLXcaSX/5Exa3gGjFANQw4qashgxUekQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.2102.18", + "@babel/core": "7.29.7", + "@babel/helper-annotate-as-pure": "7.27.3", + "@babel/helper-split-export-declaration": "7.24.7", + "@inquirer/confirm": "5.1.21", + "@vitejs/plugin-basic-ssl": "2.1.4", + "beasties": "0.4.1", + "browserslist": "^4.26.0", + "esbuild": "0.28.1", + "https-proxy-agent": "7.0.6", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "listr2": "9.0.5", + "magic-string": "0.30.21", + "mrmime": "2.0.1", + "parse5-html-rewriting-stream": "8.0.0", + "picomatch": "4.0.4", + "piscina": "5.2.0", + "rolldown": "1.0.0-rc.4", + "sass": "1.97.3", + "semver": "7.7.4", + "source-map-support": "0.5.21", + "tinyglobby": "0.2.15", + "undici": "7.28.0", + "vite": "7.3.6", + "watchpack": "2.5.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "lmdb": "3.5.1" + }, + "peerDependencies": { + "@angular/compiler": "^21.0.0", + "@angular/compiler-cli": "^21.0.0", + "@angular/core": "^21.0.0", + "@angular/localize": "^21.0.0", + "@angular/platform-browser": "^21.0.0", + "@angular/platform-server": "^21.0.0", + "@angular/service-worker": "^21.0.0", + "@angular/ssr": "^21.2.18", + "karma": "^6.4.0", + "less": "^4.2.0", + "ng-packagr": "^21.0.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.9 <6.0", + "vitest": "^4.0.8" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, + "@angular/localize": { + "optional": true + }, + "@angular/platform-browser": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "karma": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "apps/angular/node_modules/@angular/cli": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.18.tgz", + "integrity": "sha512-WOZF4p0SnrmBpNeiOA2pdp9OBvGnOSLWbGdRLXJykyseIiEn7+N+GZp+ZA1aNZB4SWolpYCZfTYwkpo6Sf/LXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2102.18", + "@angular-devkit/core": "21.2.18", + "@angular-devkit/schematics": "21.2.18", + "@inquirer/prompts": "7.10.1", + "@listr2/prompt-adapter-inquirer": "3.0.5", + "@modelcontextprotocol/sdk": "1.26.0", + "@schematics/angular": "21.2.18", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.48.1", + "ini": "6.0.0", + "jsonc-parser": "3.3.1", + "listr2": "9.0.5", + "npm-package-arg": "13.0.2", + "pacote": "21.5.1", + "parse5-html-rewriting-stream": "8.0.0", + "semver": "7.7.4", + "yargs": "18.0.0", + "zod": "4.3.6" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "apps/angular/node_modules/@angular/common": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.18.tgz", + "integrity": "sha512-gZugZ8gX/KkACIZ3/ekoImLu5z8a0Iu9O5b84kh8e+++VUjmkmd19IygBsq/8/fF3vKf0UcIKcx3P6A/gb2DJw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "21.2.18", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "apps/angular/node_modules/@angular/compiler": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.18.tgz", + "integrity": "sha512-ccnDuKLuzIa0ayijR+alarsHNWIksuGV/lxGTZ0t6/0+B6J/RXupz6M2IO6ZHHEcg8r7pcrLCUBTyp3FoiRJrg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "apps/angular/node_modules/@angular/compiler-cli": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.18.tgz", + "integrity": "sha512-L5zbIp7YfTTB4I4xT33FgEBanzhppNejzZX0HJOEqKDyDL2jXq+flt83lE0XVuRJ6/zrG+UKj0B+y/CK6Ro7Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.29.7", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^5.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.2.18", + "typescript": ">=5.9 <6.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "apps/angular/node_modules/@angular/core": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.18.tgz", + "integrity": "sha512-AG4bb6GU0+qp+vVBjc/IhSPjgcRX8QsCb8jzN8dDyhnjsyuuG/LlIiU0l6n65BI9SGKE9m6TfsJ1ObkkAiE5KQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "21.2.18", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0 || ~0.16.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "apps/angular/node_modules/@angular/platform-browser": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.18.tgz", + "integrity": "sha512-zltF+3HrlgtZbYg8U98LhG0dyGm1aHT3Px8//9wjqi/TUY8UlHsYKByL197QtVWDBjf/dekzXdz5c+iyVtanLQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/animations": "21.2.18", + "@angular/common": "21.2.18", + "@angular/core": "21.2.18" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "apps/angular/node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "apps/angular/node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "apps/angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "apps/angular/node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "apps/angular/node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "apps/angular/node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "apps/angular/node_modules/@listr2/prompt-adapter-inquirer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz", + "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 8", + "listr2": "9.0.5" + } + }, + "apps/angular/node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.1.tgz", + "integrity": "sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.1.tgz", + "integrity": "sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.1.tgz", + "integrity": "sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.1.tgz", + "integrity": "sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.1.tgz", + "integrity": "sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.1.tgz", + "integrity": "sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "apps/angular/node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.1.tgz", + "integrity": "sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "apps/angular/node_modules/@modelcontextprotocol/sdk": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "apps/angular/node_modules/@oxc-project/types": { + "version": "0.113.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.113.0.tgz", + "integrity": "sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "apps/angular/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz", + "integrity": "sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.4.tgz", + "integrity": "sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.4.tgz", + "integrity": "sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.4.tgz", + "integrity": "sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.4.tgz", + "integrity": "sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.4.tgz", + "integrity": "sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.4.tgz", + "integrity": "sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.4.tgz", + "integrity": "sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.4.tgz", + "integrity": "sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.4.tgz", + "integrity": "sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.4.tgz", + "integrity": "sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.4.tgz", + "integrity": "sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.4.tgz", + "integrity": "sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "apps/angular/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz", + "integrity": "sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==", + "dev": true, + "license": "MIT" + }, + "apps/angular/node_modules/@schematics/angular": { + "version": "21.2.18", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.18.tgz", + "integrity": "sha512-XNEWvJSUg/ljLXgGVs7picBOtzVC4abBf2+i/ds9E7GaYYVyH19Mpgk6BOuMHAgRpjPWBAeWiBkTqetiR2Ps5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "21.2.18", + "@angular-devkit/schematics": "21.2.18", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "apps/angular/node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz", + "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, + "apps/angular/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "apps/angular/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "apps/angular/node_modules/algoliasearch": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.48.1.tgz", + "integrity": "sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.14.1", + "@algolia/client-abtesting": "5.48.1", + "@algolia/client-analytics": "5.48.1", + "@algolia/client-common": "5.48.1", + "@algolia/client-insights": "5.48.1", + "@algolia/client-personalization": "5.48.1", + "@algolia/client-query-suggestions": "5.48.1", + "@algolia/client-search": "5.48.1", + "@algolia/ingestion": "1.48.1", + "@algolia/monitoring": "1.48.1", + "@algolia/recommend": "5.48.1", + "@algolia/requester-browser-xhr": "5.48.1", + "@algolia/requester-fetch": "5.48.1", + "@algolia/requester-node-http": "5.48.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "apps/angular/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "apps/angular/node_modules/beasties": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.1.tgz", + "integrity": "sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^6.0.0", + "css-what": "^7.0.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^10.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", + "postcss-media-query-parser": "^0.2.3", + "postcss-safe-parser": "^7.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "apps/angular/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "apps/angular/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "apps/angular/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "apps/angular/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "apps/angular/node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "apps/angular/node_modules/listr2/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "apps/angular/node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular/node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "apps/angular/node_modules/lmdb": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.1.tgz", + "integrity": "sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@harperfast/extended-iterable": "^1.0.3", + "msgpackr": "^1.11.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.5.3", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.5.1", + "@lmdb/lmdb-darwin-x64": "3.5.1", + "@lmdb/lmdb-linux-arm": "3.5.1", + "@lmdb/lmdb-linux-arm64": "3.5.1", + "@lmdb/lmdb-linux-x64": "3.5.1", + "@lmdb/lmdb-win32-arm64": "3.5.1", + "@lmdb/lmdb-win32-x64": "3.5.1" + } + }, + "apps/angular/node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "apps/angular/node_modules/ora": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz", + "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.3.1", + "string-width": "^8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "apps/angular/node_modules/parse5-html-rewriting-stream": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz", + "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0", + "parse5": "^8.0.0", + "parse5-sax-parser": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "apps/angular/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "apps/angular/node_modules/rolldown": { + "version": "1.0.0-rc.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.4.tgz", + "integrity": "sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.113.0", + "@rolldown/pluginutils": "1.0.0-rc.4" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.4", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.4", + "@rolldown/binding-darwin-x64": "1.0.0-rc.4", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.4", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.4", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.4", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.4", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.4", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.4", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.4", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.4", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.4" + } + }, + "apps/angular/node_modules/sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "apps/angular/node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "apps/angular/node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "apps/angular/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "apps/angular/node_modules/undici": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "apps/angular/node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "apps/angular/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "apps/angular/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "apps/angular/node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "apps/angular/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "apps/angular/node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "apps/astro": { + "name": "@svg-sprites-fixtures/astro", + "version": "0.0.0", + "dependencies": { + "astro": "7.0.7" + }, + "devDependencies": { + "@astrojs/check": "0.9.9", + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2" + } + }, + "apps/next-app": { + "name": "@svg-sprites-fixtures/next-app", + "version": "0.0.0", + "extraneous": true, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/next-app-turbopack": { + "name": "@svg-sprites-fixtures/next-app-turbopack", + "version": "0.0.0", + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/next-app-webpack": { + "name": "@svg-sprites-fixtures/next-app-webpack", + "version": "0.0.0", + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/next-pages": { + "name": "@svg-sprites-fixtures/next-pages", + "version": "0.0.0", + "extraneous": true, + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/next-pages-turbopack": { + "name": "@svg-sprites-fixtures/next-pages-turbopack", + "version": "0.0.0", + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/next-pages-webpack": { + "name": "@svg-sprites-fixtures/next-pages-webpack", + "version": "0.0.0", + "dependencies": { + "next": "16.2.10", + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/node": "24.12.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "typescript": "6.0.2" + } + }, + "apps/nuxt": { + "name": "@svg-sprites-fixtures/nuxt", + "version": "0.0.0", + "dependencies": { + "nuxt": "4.3.1", + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vue-tsc": "3.3.7" + } + }, + "apps/react-vite": { + "name": "@svg-sprites-fixtures/react-vite", + "version": "0.0.0", + "dependencies": { + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@vitejs/plugin-react": "6.0.1", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/react-webpack": { + "name": "@svg-sprites-fixtures/react-webpack", + "version": "0.0.0", + "dependencies": { + "react": "19.2.5", + "react-dom": "19.2.5" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "css-loader": "7.1.4", + "html-webpack-plugin": "5.6.7", + "style-loader": "4.0.0", + "ts-loader": "9.6.2", + "typescript": "6.0.2", + "webpack": "5.108.4", + "webpack-cli": "7.2.1", + "webpack-dev-server": "5.2.2" + } + }, + "apps/solid-vite": { + "name": "@svg-sprites-fixtures/solid-vite", + "version": "0.0.0", + "dependencies": { + "solid-js": "1.9.14" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4", + "vite-plugin-solid": "2.11.12" + } + }, + "apps/svelte-vite": { + "name": "@svg-sprites-fixtures/svelte-vite", + "version": "0.0.0", + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte-check": "4.7.2", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/sveltekit": { + "name": "@svg-sprites-fixtures/sveltekit", + "version": "0.0.0", + "dependencies": { + "svelte": "5.56.4" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@sveltejs/adapter-node": "5.5.7", + "@sveltejs/kit": "2.69.2", + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte-check": "4.7.2", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/vanilla-vite": { + "name": "@svg-sprites-fixtures/vanilla-vite", + "version": "0.0.0", + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "typescript": "6.0.2", + "vite": "8.1.4" + } + }, + "apps/vue-vite": { + "name": "@svg-sprites-fixtures/vue-vite", + "version": "0.0.0", + "dependencies": { + "vue": "3.5.39" + }, + "devDependencies": { + "@gromlab/svg-sprites": "file:../../..", + "@vitejs/plugin-vue": "6.0.7", + "typescript": "6.0.2", + "vite": "8.1.4", + "vue-tsc": "3.3.7" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.9.tgz", + "integrity": "sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^17.7.2" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/check/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@astrojs/check/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@astrojs/check/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@astrojs/check/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@astrojs/check/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@astrojs/check/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@astrojs/check/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@astrojs/check/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@astrojs/check/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@astrojs/check/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@astrojs/check/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.1.tgz", + "integrity": "sha512-DaAUj29AIBU2XdJ8uwcab8lW5O2pk9pY8AXkcMw0sw77nVa3oeTYRcO+Dvbbpoexf6ThMc0FMWYCQ/wN1/T7oQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.1", + "@astrojs/compiler-binding-darwin-x64": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.1" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.1.tgz", + "integrity": "sha512-IEmEF2fUIlTHtpeE/isyEGVOB14cEyh/LZOFYt6wn3jNyVpdC8aR5OZ+RzFUR/f+8ZDM1LaMwZKvoA7eMyJeFw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.1.tgz", + "integrity": "sha512-GF2kIxjpPDLsn94zbZNMsxEmkU828QqnmM7kiQJnaooS3jmI+I7kk6+oI6EpwOsK3femCMdcm+wmOsEqtGrmjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.1.tgz", + "integrity": "sha512-XJL3SDmOtVrqFhCirNcHwE91+IesJqlgNo23I4qW9QUYfwzm/TBZuH61fgqsb1ttgR1mMYz6ooPWs0JDhwMqpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.1.tgz", + "integrity": "sha512-xqE8BVbDoBueK/B47w30PtkVofUWJKGkwoMVE+EOMLf11rnoANxIAdA9FPqY+rng4oNI5ndHGsri1yPj2k8vZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.1.tgz", + "integrity": "sha512-1y0StU1qiCuDFH3rmbRJXcxdfHxFPrES1Rd+RLffosvUR7I2cH5SF5SFnBN9vXpzpkmyElZm3Yr47iJBPN7vVA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.1.tgz", + "integrity": "sha512-16q0fYf7kpbmdObZEeZJEup8hQv/whgNwVjrSvT8umrKwLDSnNIWiQpm09lQQu6bweZB0XyIvHwlPitvJhC+hg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.1.tgz", + "integrity": "sha512-cB456shIwDv/PrVT+2QG7LFndpHkVge5HjqADKZgGaAc9JHVktCtjSrcdkRQ+3tbkPazNKaTLRjXLIiz2NIx9g==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.1.tgz", + "integrity": "sha512-ur/9+If/yTE69mmeX5MqSZndL0HOyx67GeNZUy3N7wVdWpLz9UTJXwyWS4UR2PUQHitghjsM5xoX0Ge56WRVQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.1.tgz", + "integrity": "sha512-k0W+kDBzDkNZOqu4kElDvCOIbKw5Ut9S1WZ1Krj3KTgNuBERNKXsMMsRLLcbgfdMdbe7bTekQLshZrrvmYpmwA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.1.tgz", + "integrity": "sha512-aT7xkgsbNoS6nriY5qKpbihK43slFHO41iqgHCTdOvn1ifaQxLCc5yXy+6GzAtiafoaC1zA7OwVXCXMsvUZOkg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.1.tgz", + "integrity": "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.1.1", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.16.11", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.11.tgz", + "integrity": "sha512-sJ/EfnFp0+gurTrkvONtd9qRqmMZLT9bHelfI1SA35CaQVTrRrA74qteOcNT/al1b9Atg3IiH1Jk/qfckyC+fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.1", + "@astrojs/yaml2ts": "^0.2.4", + "@jridgewell/sourcemap-codec": "^1.5.5", + "@volar/kit": "~2.4.28", + "@volar/language-core": "~2.4.28", + "@volar/language-server": "~2.4.28", + "@volar/language-service": "~2.4.28", + "muggle-string": "^0.4.1", + "tinyglobby": "^0.2.16", + "volar-service-css": "0.0.71", + "volar-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.1.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.3.tgz", + "integrity": "sha512-Lje33Ittd8UQGgbIIWQvhPkj5X5c4b1sZnZWX3JQV/AWpfbuQGxVi2ONt6+ScydcwfR4egilslEWyczMclrJ1g==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", + "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", + "license": "MIT OR Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@colordx/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.5.0.tgz", + "integrity": "sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==", + "license": "MIT" + }, + "node_modules/@discoveryjs/json-ext": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", + "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@dxup/nuxt": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@dxup/nuxt/-/nuxt-0.3.2.tgz", + "integrity": "sha512-2f2usP4oLNsIGjPprvABe3f3GWuIhIDp0169pGLFxTDRI5A4d4sBbGpR+tD9bGZCT+1Btb6Q2GKlyv3LkDCW5g==", + "license": "MIT", + "dependencies": { + "@dxup/unimport": "^0.1.2", + "@nuxt/kit": "^4.2.2", + "chokidar": "^5.0.0", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.15" + } + }, + "node_modules/@dxup/unimport": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@dxup/unimport/-/unimport-0.1.2.tgz", + "integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==", + "license": "MIT" + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@gar/promise-retry": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", + "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@gromlab/svg-sprites": { + "resolved": "..", + "link": true + }, + "node_modules/@harperfast/extended-iterable": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz", + "integrity": "sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-core": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.64.0.tgz", + "integrity": "sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "@jsonjoy.com/fs-node-utils": "4.64.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.64.0.tgz", + "integrity": "sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.64.0", + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "@jsonjoy.com/fs-node-utils": "4.64.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.64.0.tgz", + "integrity": "sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.64.0", + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "@jsonjoy.com/fs-node-utils": "4.64.0", + "@jsonjoy.com/fs-print": "4.64.0", + "@jsonjoy.com/fs-snapshot": "4.64.0", + "glob-to-regex.js": "^1.0.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.64.0.tgz", + "integrity": "sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.64.0.tgz", + "integrity": "sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.64.0", + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "@jsonjoy.com/fs-node-utils": "4.64.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.64.0.tgz", + "integrity": "sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "glob-to-regex.js": "^1.0.1" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-print": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.64.0.tgz", + "integrity": "sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.64.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.64.0.tgz", + "integrity": "sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.64.0", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@next/env": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", + "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", + "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", + "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", + "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", + "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", + "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", + "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", + "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", + "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.2.tgz", + "integrity": "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.2.tgz", + "integrity": "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.5.tgz", + "integrity": "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.4.tgz", + "integrity": "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@nuxt/cli": { + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@nuxt/cli/-/cli-3.36.1.tgz", + "integrity": "sha512-qu0LLFjOr1alMSrcVb3k3fWYXGeETeOIN5UoGoGEjwWVtf+KxQbN5IAzdXb7ip5D3pfKLcrAOTbIGf9XFmeGFQ==", + "license": "MIT", + "dependencies": { + "@bomb.sh/tab": "^0.0.17", + "@clack/prompts": "^1.6.0", + "c12": "^3.3.4", + "citty": "^0.2.2", + "confbox": "^0.2.4", + "consola": "^3.4.2", + "debug": "^4.4.3", + "defu": "^6.1.7", + "exsolve": "^1.0.8", + "fuse.js": "^7.4.2", + "fzf": "^0.5.2", + "giget": "^3.3.0", + "jiti": "^2.7.0", + "listhen": "^1.10.0", + "nypm": "^0.6.7", + "ofetch": "^1.5.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.1", + "scule": "^1.3.0", + "semver": "^7.8.5", + "srvx": "^0.11.17", + "std-env": "^4.1.0", + "tinyclip": "^0.1.14", + "tinyexec": "^1.2.4", + "ufo": "^1.6.4", + "youch": "^4.1.1" + }, + "bin": { + "nuxi": "bin/nuxi.mjs", + "nuxi-ng": "bin/nuxi.mjs", + "nuxt": "bin/nuxi.mjs", + "nuxt-cli": "bin/nuxi.mjs" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + }, + "peerDependencies": { + "@nuxt/schema": "^4.4.6" + }, + "peerDependenciesMeta": { + "@nuxt/schema": { + "optional": true + } + } + }, + "node_modules/@nuxt/cli/node_modules/@bomb.sh/tab": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@bomb.sh/tab/-/tab-0.0.17.tgz", + "integrity": "sha512-rGhqfWwaSF6qN5Gm5P9EH9eybrwLEowHTkV+wsRgFewT6aQCQWVWXLclVk0McgVIlWCGX+W9mYYC1Egsg+Znsw==", + "license": "MIT", + "bin": { + "tab": "dist/bin/cli.mjs" + }, + "peerDependencies": { + "cac": "^6.7.14", + "citty": "^0.1.6 || ^0.2.0", + "commander": "^13.1.0" + }, + "peerDependenciesMeta": { + "cac": { + "optional": true + }, + "citty": { + "optional": true + }, + "commander": { + "optional": true + } + } + }, + "node_modules/@nuxt/cli/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nuxt/cli/node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "license": "MIT" + }, + "node_modules/@nuxt/devalue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nuxt/devalue/-/devalue-2.0.2.tgz", + "integrity": "sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==", + "license": "MIT" + }, + "node_modules/@nuxt/devtools": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@nuxt/devtools/-/devtools-3.2.4.tgz", + "integrity": "sha512-VPbFy7hlPzWpEZk4BsuVpNuHq1ZYGV9xezjb7/NGuePuNLqeNn74YZugU+PCtva7OwKhEeTXmMK0Mqo/6+nwNA==", + "license": "MIT", + "dependencies": { + "@nuxt/devtools-kit": "3.2.4", + "@nuxt/devtools-wizard": "3.2.4", + "@nuxt/kit": "^4.4.2", + "@vue/devtools-core": "^8.1.0", + "@vue/devtools-kit": "^8.1.0", + "birpc": "^4.0.0", + "consola": "^3.4.2", + "destr": "^2.0.5", + "error-stack-parser-es": "^1.0.5", + "execa": "^8.0.1", + "fast-npm-meta": "^1.4.2", + "get-port-please": "^3.2.0", + "hookable": "^6.1.0", + "image-meta": "^0.2.2", + "is-installed-globally": "^1.0.0", + "launch-editor": "^2.13.1", + "local-pkg": "^1.1.2", + "magicast": "^0.5.2", + "nypm": "^0.6.5", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "semver": "^7.7.4", + "simple-git": "^3.33.0", + "sirv": "^3.0.2", + "structured-clone-es": "^2.0.0", + "tinyglobby": "^0.2.15", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-tracer": "^1.3.0", + "which": "^6.0.1", + "ws": "^8.19.0" + }, + "bin": { + "devtools": "cli.mjs" + }, + "peerDependencies": { + "@vitejs/devtools": "*", + "vite": ">=6.0" + }, + "peerDependenciesMeta": { + "@vitejs/devtools": { + "optional": true + } + } + }, + "node_modules/@nuxt/devtools-kit": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@nuxt/devtools-kit/-/devtools-kit-3.2.4.tgz", + "integrity": "sha512-Yxy2Xgmq5hf3dQy983V0xh0OJV2mYwRZz9eVIGc3EaribdFGPDNGMMbYqX9qCty3Pbxn/bCF3J0UyPaNlHVayQ==", + "license": "MIT", + "dependencies": { + "@nuxt/kit": "^4.4.2", + "execa": "^8.0.1" + }, + "peerDependencies": { + "vite": ">=6.0" + } + }, + "node_modules/@nuxt/devtools-kit/node_modules/@nuxt/kit": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.8.tgz", + "integrity": "sha512-ZUlZ5iYfyfJFDPluhn6ZxFWcsuxWbLnZBc8w3MAROcQ4lYfZ+qFpALBLSNlpc0zhOa++33EE+5PEbOAdVIY+dw==", + "license": "MIT", + "dependencies": { + "c12": "^3.3.4", + "consola": "^3.4.2", + "defu": "^6.1.7", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.8", + "ignore": "^7.0.5", + "jiti": "^2.7.0", + "klona": "^2.0.6", + "mlly": "^1.8.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.1", + "rc9": "^3.0.1", + "scule": "^1.3.0", + "semver": "^7.8.1", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "unctx": "^2.5.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/devtools-kit/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nuxt/devtools-kit/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/@nuxt/devtools-wizard": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@nuxt/devtools-wizard/-/devtools-wizard-3.2.4.tgz", + "integrity": "sha512-5tu2+Quu9XTxwtpzM8CUN0UKn/bzZIfJcoGd+at5Yy1RiUQJ4E52tRK0idW1rMSUDkbkvX3dSnu8Tpj7SAtWdQ==", + "license": "MIT", + "dependencies": { + "@clack/prompts": "^1.1.0", + "consola": "^3.4.2", + "diff": "^8.0.3", + "execa": "^8.0.1", + "magicast": "^0.5.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "semver": "^7.7.4" + }, + "bin": { + "devtools-wizard": "cli.mjs" + } + }, + "node_modules/@nuxt/devtools/node_modules/@nuxt/kit": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.8.tgz", + "integrity": "sha512-ZUlZ5iYfyfJFDPluhn6ZxFWcsuxWbLnZBc8w3MAROcQ4lYfZ+qFpALBLSNlpc0zhOa++33EE+5PEbOAdVIY+dw==", + "license": "MIT", + "dependencies": { + "c12": "^3.3.4", + "consola": "^3.4.2", + "defu": "^6.1.7", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.8", + "ignore": "^7.0.5", + "jiti": "^2.7.0", + "klona": "^2.0.6", + "mlly": "^1.8.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.1", + "rc9": "^3.0.1", + "scule": "^1.3.0", + "semver": "^7.8.1", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "unctx": "^2.5.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/devtools/node_modules/hookable": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz", + "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==", + "license": "MIT" + }, + "node_modules/@nuxt/devtools/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/@nuxt/devtools/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nuxt/devtools/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/@nuxt/devtools/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@nuxt/kit": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.3.1.tgz", + "integrity": "sha512-UjBFt72dnpc+83BV3OIbCT0YHLevJtgJCHpxMX0YRKWLDhhbcDdUse87GtsQBrjvOzK7WUNUYLDS/hQLYev5rA==", + "license": "MIT", + "dependencies": { + "c12": "^3.3.3", + "consola": "^3.4.2", + "defu": "^6.1.4", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.8", + "ignore": "^7.0.5", + "jiti": "^2.6.1", + "klona": "^2.0.6", + "mlly": "^1.8.0", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "rc9": "^3.0.0", + "scule": "^1.3.0", + "semver": "^7.7.4", + "tinyglobby": "^0.2.15", + "ufo": "^1.6.3", + "unctx": "^2.5.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/nitro-server": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@nuxt/nitro-server/-/nitro-server-4.3.1.tgz", + "integrity": "sha512-4aNiM69Re02gI1ywnDND0m6QdVKXhWzDdtvl/16veytdHZj3FSq57ZCwOClNJ7HQkEMqXgS+bi6S2HmJX+et+g==", + "license": "MIT", + "dependencies": { + "@nuxt/devalue": "^2.0.2", + "@nuxt/kit": "4.3.1", + "@unhead/vue": "^2.1.3", + "@vue/shared": "^3.5.27", + "consola": "^3.4.2", + "defu": "^6.1.4", + "destr": "^2.0.5", + "devalue": "^5.6.2", + "errx": "^0.1.0", + "escape-string-regexp": "^5.0.0", + "exsolve": "^1.0.8", + "h3": "^1.15.5", + "impound": "^1.0.0", + "klona": "^2.0.6", + "mocked-exports": "^0.1.1", + "nitropack": "^2.13.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "rou3": "^0.7.12", + "std-env": "^3.10.0", + "ufo": "^1.6.3", + "unctx": "^2.5.0", + "unstorage": "^1.17.4", + "vue": "^3.5.27", + "vue-bundle-renderer": "^2.2.0", + "vue-devtools-stub": "^0.1.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "nuxt": "^4.3.1" + } + }, + "node_modules/@nuxt/telemetry": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@nuxt/telemetry/-/telemetry-2.8.0.tgz", + "integrity": "sha512-zAwXY24KYvpLTmiV+osagd2EHkfs5IF+7oDZYTQoit5r0kPlwaCNlzHp5I/wUAWT4LBw6lG8gZ6bWidAdv/erQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.1", + "consola": "^3.4.2", + "ofetch": "^2.0.0-alpha.3", + "rc9": "^3.0.0", + "std-env": "^4.0.0" + }, + "bin": { + "nuxt-telemetry": "bin/nuxt-telemetry.mjs" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "@nuxt/kit": ">=3.0.0" + } + }, + "node_modules/@nuxt/telemetry/node_modules/ofetch": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-2.0.0-alpha.3.tgz", + "integrity": "sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==", + "license": "MIT" + }, + "node_modules/@nuxt/telemetry/node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "license": "MIT" + }, + "node_modules/@nuxt/vite-builder": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@nuxt/vite-builder/-/vite-builder-4.3.1.tgz", + "integrity": "sha512-LndnxPJzDUDbWAB8q5gZZN1mSOLHEyMOoj4T3pTdPydGf31QZdMR0V1fQ1fdRgtgNtWB3WLP0d1ZfaAOITsUpw==", + "license": "MIT", + "dependencies": { + "@nuxt/kit": "4.3.1", + "@rollup/plugin-replace": "^6.0.3", + "@vitejs/plugin-vue": "^6.0.4", + "@vitejs/plugin-vue-jsx": "^5.1.4", + "autoprefixer": "^10.4.24", + "consola": "^3.4.2", + "cssnano": "^7.1.2", + "defu": "^6.1.4", + "esbuild": "^0.27.3", + "escape-string-regexp": "^5.0.0", + "exsolve": "^1.0.8", + "get-port-please": "^3.2.0", + "jiti": "^2.6.1", + "knitwork": "^1.3.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "mocked-exports": "^0.1.1", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "postcss": "^8.5.6", + "rollup-plugin-visualizer": "^6.0.5", + "seroval": "^1.5.0", + "std-env": "^3.10.0", + "ufo": "^1.6.3", + "unenv": "^2.0.0-rc.24", + "vite": "^7.3.1", + "vite-node": "^5.3.0", + "vite-plugin-checker": "^0.12.0", + "vue-bundle-renderer": "^2.2.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "nuxt": "4.3.1", + "rolldown": "^1.0.0-beta.38", + "vue": "^3.3.4" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + } + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/@nuxt/vite-builder/node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-minify/binding-android-arm-eabi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz", + "integrity": "sha512-m7TGBR2hjsBJIN9UJ909KBoKsuogo6CuLsHKvUIBXdjI0JVHP8g4ZHeB+BJpGn5LJdeSGDfz9MWiuXrZDRzunw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-android-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz", + "integrity": "sha512-RvxOOkzvP5NeeoraBtgNJSBqO+XzlS7DooxST/drAXCfO52GsmxVB1N7QmifrsTYtH8GC2z3DTFjZQ1w/AJOWg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz", + "integrity": "sha512-hDslO3uVHza3kB9zkcsi25JzN65Gj5ZYty0OvylS11Mhg9ydCYxAzfQ/tISHW/YmV1NRUJX8+GGqM1cKmrHaTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz", + "integrity": "sha512-mWA2Y5bUyNoGM+gSGGHesgtQ3LDWgpRe4zDGkBDovxNIiDLBXqu/7QcuS+G918w8oG9VYm1q1iinILer/2pD1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-freebsd-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz", + "integrity": "sha512-T7fsegxcy82xS0jWPXkz/BMhrkb3D7YOCiV0R9pDksjaov+iIFoNEWAoBsaC5NtpdzkX+bmffwDpu336EIfEeg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-gnueabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz", + "integrity": "sha512-yePavbIilAcpVYc8vRsDCn3xJxHMXDZIiamyH9fuLosAHNELcLib4/JR4fhDk4NmHVagQH3kRhsnm5Q9cm3pAw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-musleabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz", + "integrity": "sha512-lmPWLXtW6FspERhy97iP0hwbmLtL66xI29QQ9GpHmTiE4k+zv/FaefuV/Qw+LuHnmFSYzUNrLcxh4ulOZTIP2g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz", + "integrity": "sha512-gySS5XqU5MKs/oCjsTlVm8zb8lqcNKHEANsaRmhW2qvGKJoeGwFb6Fbq6TLCZMRuk143mLbncbverBCa1c3dog==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz", + "integrity": "sha512-IRFMZX589lr3rjG0jc8N261/7wqFq2Vl0OMrJWeFls5BF8HiB+fRYuf0Zy2CyRH6NCY2vbdDdp+QCAavQGVsGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-ppc64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz", + "integrity": "sha512-V/69XqIW9hCUceDpcZh79oDg+F4ptEgIfKRENzYs41LRbSoJ7sNjjcW4zifqyviTvzcnXLgK4uoTyoymmNZBMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-riscv64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz", + "integrity": "sha512-zghvexySyGXGNW+MutjZN7UGTyOQl56RWMlPe1gb+knBm/+0hf9qjk7Q6ofm2tSte+vQolPfQttifGl0dP9uvQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-riscv64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz", + "integrity": "sha512-E4a8VUFDJPb2mPcc7J4NQQPi1ssHKF7/g4r6KD2+SBVERIaEEd3cGNqR7SG3g82/BLGV2UDoQe/WvZCkt5M/bQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-s390x-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz", + "integrity": "sha512-2Hx87sK3y6jBV364Mvv0zyxiITIuy26Ixenv6pK7e+4an3HgNdhAj8nk3aLoLTTSvLik5/MaGhcZGEu9tYV1aA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-x64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz", + "integrity": "sha512-2MSCnEPLk9ddSouMhJo78Xy2/JbYC80OYzWdR4yWTGSULsgH3d1VXg73DSwFL8vU7Ad9oK10DioBY2ww7sQTEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-x64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz", + "integrity": "sha512-HAPfmQKlkVi97/zRonVE9t/kKUG3ni+mOuU1Euw+3s37KwUuOJjmcwXdclVgXKBlTkCGO0FajPwW5dAJeIXCCw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-openharmony-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz", + "integrity": "sha512-bLnMojcPadYzMNpB6IAqMiTOag4etc0zbs8On73JsotO1W5c5/j/ncplpSokpEpNasKRUpHVRXpmq0KRXprNhw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-wasm32-wasi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz", + "integrity": "sha512-tv7PmHYq/8QBlqMaDjsy51GF5KQkG17Yc/PsgB5OVndU34kwbQuebBIic7UfK9ygzidI8moYq3ztnu3za/rqHw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-win32-arm64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz", + "integrity": "sha512-d+jes2jwRkcBSpcaZC6cL8GBi56Br6uAorn9dfquhWLczWL+hHSvvVrRgT1i5/6dkf5UWx2zdoEsAMiJ11w78A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-win32-ia32-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz", + "integrity": "sha512-TV1C3qDwj7//jNIi5tnNRhReSUgtaRQKi5KobDE6zVAc5gjeuBA8G2qizS9ziXlf/I0dlelrGmGMMDJmH9ekWg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-win32-x64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz", + "integrity": "sha512-LML2Gld6VY8/+7a3VH4k1qngsBXvTkXgbmYgSYwaElqtiQiYaAcXfi0XKOUGe3k3GbBK4juAGixC31CrdFHAQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm-eabi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz", + "integrity": "sha512-retxBzJ39Da7Lh/eZTn9+HJgTeDUxZIpuI0urOsmcFsBKXAth3lc1jIvwseQ9qbAI/VrsoFOXiGIzgclARbAHg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz", + "integrity": "sha512-pRkbBRbuIIsufUWpOJ+JHWfJFNupkidy4sbjfcm37e6xwYrn9LSKMLubPHvNaL1Zf92ZRhGiwaYkEcmaFg2VcA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz", + "integrity": "sha512-fh6/KQL/cbH5DukT3VkdCqnULLuvVnszVKySD5IgSE0WZb32YZo/cPsPdEv052kk6w3N4agu+NTiMnZjcvhUIg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz", + "integrity": "sha512-vUBOOY1E30vlu/DoTGDoT1UbLlwu5Yv9tqeBabAwRzwNDz8Skho16VKhsBDUiyqddtpsR3//v6vNk38w4c+6IA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz", + "integrity": "sha512-hnEtO/9AVnYWzrgnp6L+oPs/6UqlFeteUL6n7magkd2tttgmx1C01hyNNh6nTpZfLzEVJSNJ0S+4NTsK2q2CxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz", + "integrity": "sha512-WxJrUz3pcIc2hp4lvJbvt/sTL33oX9NPvkD3vDDybE6tc0V++rS+hNOJxwXdD2FDIFPkHs/IEn5asEZFVH+VKw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz", + "integrity": "sha512-jj8A8WWySaJQqM9XKAIG8U2Q3qxhFQKrXPWv98d1oC35at+L1h+C+V4M3l8BAKhpHKCu3dYlloaAbHd5q1Hw6A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz", + "integrity": "sha512-G2F8H6FcAExVK5vvhpSh61tqWx5QoaXXUnSsj5FyuDiFT/K7AMMVSQVqnZREDc+YxhrjB0vnKjCcuobXK63kIw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz", + "integrity": "sha512-3R0iqjM3xYOZCnwgcxOQXH7hrz64/USDIuLbNTM1kZqQzRqaR4w7SwoWKU934zABo8d0op2oSwOp+CV3hZnM7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz", + "integrity": "sha512-lAQf8PQxfgy7h0bmcfSVE3hg3qMueshPYULFsCrHM+8KefGZ9W+ZMvRyU33gLrB4w1O3Fz1orR0hmKMCRxXNrQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz", + "integrity": "sha512-2QlvQBUhHuAE3ezD4X3CAEKMXdfgInggQ5Bj/7gb5NcYP3GyfLTj7c+mMu+BRwfC9B3AXBNyqHWbqEuuUvZyRQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz", + "integrity": "sha512-v06iu0osHszgqJ1dLQRb6leWFU1sjG/UQk4MoVBtE6ZPewgfTkby6G9II1SpEAf2onnAuQceVYxQH9iuU3NJqw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz", + "integrity": "sha512-+5HhNHtxsdcd7+ljXFnn9FOoCNXJX3UPgIfIE6vdwS1HqdGNH6eAcVobuqGOp54l8pvcxDQA6F4cPswCgLrQfQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz", + "integrity": "sha512-jKwO7ZLNkjxwg7FoCLw+fJszooL9yXRZsDN0AQ1AQUTWq1l8GH/2e44k68N3fcP19jl8O8jGpqLAZcQTYk6skA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz", + "integrity": "sha512-TYqnuKV/p3eOc+N61E0961nA7DC+gaCeJ3+V2LcjJdTwFMdikqWL6uVk1jlrpUCBrozHDATVUKDZYH7r4FQYjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-openharmony-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz", + "integrity": "sha512-ZhrVmWFifVEFQX4XPwLoVFDHw9tAWH9p9vHsHFH+5uCKdfVR+jje4WxVo6YrokWCboGckoOzHq5KKMOcPZfkRg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz", + "integrity": "sha512-Gr8X2PUU3hX1g3F5oLWIZB8DhzDmjr5TfOrmn5tlBOo9l8ojPGdKjnIBfObM7X15928vza8QRKW25RTR7jfivg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz", + "integrity": "sha512-t5CDLbU70Ea88bGRhvU/dLJTc/Wcrtf2Jp534E8P3cgjAvHDjdKsfDDqBZrhybJ8Jv9v9vW5ngE40EK51BluDA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-ia32-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz", + "integrity": "sha512-rZH0JynCCwnhe2HfRoyNOl/Kfd9pudoWxgpC5OZhj7j77pMK0UOAa35hYDfrtSOUk2HLzrikV5dPUOY2DpSBSA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz", + "integrity": "sha512-oGHluohzmVFAuQrkEnl1OXAxMz2aYmimxUqIgKXpBgbr7PvFv0doELB273sX+5V3fKeggohKg1A2Qq21W9Z9cQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.112.0.tgz", + "integrity": "sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxc-transform/binding-android-arm-eabi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz", + "integrity": "sha512-r4LuBaPnOAi0eUOBNi880Fm2tO2omH7N1FRrL6+nyz/AjQ+QPPLtoyZJva0O+sKi1buyN/7IzM5p9m+5ANSDbg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-android-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz", + "integrity": "sha512-ve46vQcQrY8eGe8990VSlS9gkD+AogJqbtfOkeua+5sQGQTDgeIRRxOm7ktCo19uZc2bEBwXRJITgosd+NRVmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz", + "integrity": "sha512-ddbmLU3Tr+i7MOynfwAXxUXud3SjJKlv7XNjaq08qiI8Av/QvhXVGc2bMhXkWQSMSBUeTDoiughKjK+Zsb6y/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz", + "integrity": "sha512-TKvmNw96jQZPqYb4pRrzLFDailNB3YS14KNn+x2hwRbqc6CqY96S9PYwyOpVpYdxfoRjYO9WgX9SoS+62a1DPA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-freebsd-x64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz", + "integrity": "sha512-YPMkSCDaelO8HHYRMYjm+Q+IfkfIbdtQzwPuasItYkq8UUkNeHNPheNh2JkvQa3c+io3E9ePOgHQ2yihpk7o/Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-gnueabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz", + "integrity": "sha512-nA7kzQGNEpuTRknst/IJ3l8hqmDmEda3aun6jkXgp7gKxESjuHeaNH04mKISxvJ7fIacvP2g/wtTSnm4u5jL8Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-musleabihf": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz", + "integrity": "sha512-w8GuLmckKlGc3YujaZKhtbFxziCcosvM2l9GnQjCb/yENWLGDiyQOy0BTAgPGdJwpYTiOeJblEXSuXYvlE1Ong==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz", + "integrity": "sha512-9LwwGnJ8+WT0rXcrI8M0RJtDNt91eMqcDPPEvJxhRFHIMcHTy5D5xT+fOl3Us0yMqKo3HUWkbfUYqAp4GoZ3Jw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz", + "integrity": "sha512-Lg6VOuSd3oXv7J0eGywgqh/086h+qQzIBOD+47pYKMTTJcbDe+f3h/RgGoMKJE5HhiwT5sH1aGEJfIfaYUiVSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-ppc64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz", + "integrity": "sha512-PXzmj82o1moA4IGphYImTRgc2youTi4VRfyFX3CHwLjxPcQ5JtcsgbDt4QUdOzXZ+zC07s5jf2ZzhRapEOlj2w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-riscv64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz", + "integrity": "sha512-vhJsMsVH/6xwa3bt1LGts33FXUkGjaEGDwsRyp4lIfOjSfQVWMtCmWMFNaA0dW9FVWdD2Gt2fSFBSZ+azDxlpg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-riscv64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz", + "integrity": "sha512-cXWFb7z+2IjFUEcXtRwluq9oEG5qnyFCjiu3SWrgYNcWwPdHusv3I/7K5/CTbbi4StoZ5txbi7/iSfDHNyWuRw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-s390x-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz", + "integrity": "sha512-eEFu4SRqJTJ20/88KRWmp+jpHKAw0Y1DsnSgpEeXyBIIcsOaLIUMU/TfYWUmqRbvbMV9rmOmI3kp5xWYUq6kSQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-x64-gnu": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz", + "integrity": "sha512-ST1MDT+TlOyZ1c5btrGinRSUW2Jf4Pa+0gdKwsyjDSOC3dxy2ZNkN3mosTf4ywc3J+mxfYKqtjs7zSwHz03ILA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-x64-musl": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz", + "integrity": "sha512-ISQoA3pD4cyTGpf9sXXeerH6pL2L6EIpdy6oAy2ttkswyVFDyQNVOVIGIdLZDgbpmqGljxZnWqt/J/N68pQaig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-openharmony-arm64": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz", + "integrity": "sha512-UOGVrGIv7yLJovyEXEyUTADuLq98vd/cbMHFLJweRXD+11I8Tn4jASi4WzdsN8C3BVYGRHrXH2NlSBmhz33a4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-wasm32-wasi": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz", + "integrity": "sha512-XIX7Gpq9koAvzBVHDlVFHM79r5uOVK6kTEsdsN4qaajpjkgtv4tdsAOKIYK6l7fUbsbE6xS+6w1+yRFrDeC1kg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-win32-arm64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz", + "integrity": "sha512-EgXef9kOne9BNsbYBbuRqxk2hteT0xsAGcx/VbtCBMJYNj8fANFhT271DUSOgfa4DAgrQQmsyt/Kr1aV9mpU9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-win32-ia32-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz", + "integrity": "sha512-6QaB0qjNaou2YR+blncHdw7j0e26IOwOIjLbhVGDeuf9+4rjJeiqRXJ2hOtCcS4zblnao/MjdgQuZ3fM0nl+Kw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-win32-x64-msvc": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz", + "integrity": "sha512-FRKYlY959QeqRPx9kXs0HjU2xuXPT1cdF+vvA200D9uAX/KLcC34MwRqUKTYml4kCc2Vf/P2pBR9cQuBm3zECQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.5.6.tgz", + "integrity": "sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==", + "bundleDependencies": [ + "napi-wasm" + ], + "license": "MIT", + "dependencies": { + "is-glob": "^4.0.3", + "napi-wasm": "^1.1.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "license": "MIT" + }, + "node_modules/@poppinss/colors": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.7.0.tgz", + "integrity": "sha512-0UTYalzk2t6S4rA2uHOz5bSSW2CHdv4vggJI6Alg90yvl0UgXs6XSXpH96OH+bRkX4J/06djv29pqXJ0lq5Kag==", + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/dumper/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-alias": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-6.0.0.tgz", + "integrity": "sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "rollup": ">=4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", + "license": "MIT", + "dependencies": { + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz", + "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz", + "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz", + "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz", + "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz", + "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz", + "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz", + "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/core": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.1.tgz", + "integrity": "sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.1.tgz", + "integrity": "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", + "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gar/promise-retry": "^1.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", + "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.1.tgz", + "integrity": "sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@simple-git/args-pathspec": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz", + "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==", + "license": "MIT" + }, + "node_modules/@simple-git/argv-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz", + "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==", + "license": "MIT", + "dependencies": { + "@simple-git/args-pathspec": "^1.0.3" + } + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz", + "integrity": "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==", + "license": "CC0-1.0" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz", + "integrity": "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.7.tgz", + "integrity": "sha512-uOfc9eVlI3A37RRSaKcgrheBYPrfJwC9VMqDp8x/O6tlKdcLLvHThSWD0KNIbjQ/d+7bwLGx3vx6aowAcRfd2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-replace": "^6.0.3", + "rollup": "^4.59.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.69.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.69.2.tgz", + "integrity": "sha512-CMdPDbYjRwRu4KXTxBVMuOpFPCt1i/v0ANennotec+K9Cmb2e3w2yYzJiC6Vh/WSvm9Khi5sJMZa0rJPqfHlDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.9", + "@types/cookie": "^0.6.0", + "acorn": "^8.16.0", + "cookie": "^0.6.0", + "devalue": "^5.8.1", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz", + "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.2.0.tgz", + "integrity": "sha512-1SpkuMSRLfugrVX+IrKfE1RUegzo8AQzKQ6qQPfVzbcWi5IhuTPaKb5ZrLpucleFznkc4/RTeSPoRnGWFxX+EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "magic-string": "^0.30.21", + "obug": "^2.1.0", + "vitefu": "^1.1.2" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" + } + }, + "node_modules/@svg-sprites-fixtures/angular": { + "resolved": "apps/angular", + "link": true + }, + "node_modules/@svg-sprites-fixtures/astro": { + "resolved": "apps/astro", + "link": true + }, + "node_modules/@svg-sprites-fixtures/next-app-turbopack": { + "resolved": "apps/next-app-turbopack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/next-app-webpack": { + "resolved": "apps/next-app-webpack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/next-pages-turbopack": { + "resolved": "apps/next-pages-turbopack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/next-pages-webpack": { + "resolved": "apps/next-pages-webpack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/nuxt": { + "resolved": "apps/nuxt", + "link": true + }, + "node_modules/@svg-sprites-fixtures/react-vite": { + "resolved": "apps/react-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/react-webpack": { + "resolved": "apps/react-webpack", + "link": true + }, + "node_modules/@svg-sprites-fixtures/solid-vite": { + "resolved": "apps/solid-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/svelte-vite": { + "resolved": "apps/svelte-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/sveltekit": { + "resolved": "apps/sveltekit", + "link": true + }, + "node_modules/@svg-sprites-fixtures/vanilla-vite": { + "resolved": "apps/vanilla-vite", + "link": true + }, + "node_modules/@svg-sprites-fixtures/vue-vite": { + "resolved": "apps/vue-vite", + "link": true + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.17", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz", + "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", + "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/@unhead/vue": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.15.tgz", + "integrity": "sha512-SSByXfEjhzPn8gXdEdgpYqpLMPSkLUH2HVE0GxZfOtNsJ0GgOHQs0g9T67ZZ1z0kTELLKdtOtYrzrbv9+ffF7g==", + "license": "MIT", + "dependencies": { + "hookable": "^6.0.1", + "unhead": "2.1.15" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + }, + "peerDependencies": { + "vue": ">=3.5.18" + } + }, + "node_modules/@unhead/vue/node_modules/hookable": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz", + "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==", + "license": "MIT" + }, + "node_modules/@vercel/nft": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.10.2.tgz", + "integrity": "sha512-w+WyX5Ulmj4dtTZrxaulqrjaLZHSbnPzx75SJsTNYmotKsqn1JlLnDJa+lz5hn90HJofhl/2MAtw0mCrgM3qYw==", + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^13.0.0", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", + "integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==", + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.6.tgz", + "integrity": "sha512-YXvi4as2clxt6DFw5+a0tTA97ntiQXm/raR8ofNj3aNwwdlVGTiG2gp7EvfZW17P50acL/9bP0ccF4XnqNmlgA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-syntax-typescript": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7", + "@rolldown/pluginutils": "^1.0.1", + "@vue/babel-plugin-jsx": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@vitejs/plugin-vue-jsx/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue-macros/common": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz", + "integrity": "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz", + "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==", + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz", + "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@vue/babel-helper-vue-transform-on": "2.0.1", + "@vue/babel-plugin-resolve-type": "2.0.1", + "@vue/shared": "^3.5.22" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz", + "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.4", + "@vue/compiler-sfc": "^3.5.22" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz", + "integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.39", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz", + "integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.39.tgz", + "integrity": "sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.39", + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.39.tgz", + "integrity": "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-core": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.1.5.tgz", + "integrity": "sha512-5e5jQOEssCdZA1wlFEUkIDtb+cAOWuLNWJ52fm4PBWbF7e3oTnM2fneaL42E5lJoolAaUQ678tv/XEb3h4e86Q==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.1.5", + "@vue/devtools-shared": "^8.1.5" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.5.tgz", + "integrity": "sha512-FcSAxsi4eWuXLCB7Rv9lj0aIVHHPNVQ2BazGf4RJTc2JCqb4BQg0hk87ZFhminCfl+mD5OUI0rX2cgyu4kJOGA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.1.5", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-kit/node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.5.tgz", + "integrity": "sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.7.tgz", + "integrity": "sha512-LzmkKinXAMMoh8Jfi/jMUSDUjuPdv8mynH5WJGKfXyZtDw3hQ6GBaoI6Bcnl/Xqlu32q/0Z6i/trp4VXykzyLw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.39.tgz", + "integrity": "sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.39.tgz", + "integrity": "sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.39.tgz", + "integrity": "sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.39", + "@vue/runtime-core": "3.5.39", + "@vue/shared": "3.5.39", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.39.tgz", + "integrity": "sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39" + }, + "peerDependencies": { + "vue": "3.5.39" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz", + "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "devOptional": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "license": "MIT" + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz", + "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/archiver-utils/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz", + "integrity": "sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "ast-kit": "^2.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/astro": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.0.7.tgz", + "integrity": "sha512-swqrKDSI/B83GFroYPZYMFcxqbSe9+tkynu1WDBk3GLgBfV9++qVM4Z+2uFT6uu9A53Q0TROnxLketfMEENuqQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.0", + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/markdown-satteri": "0.3.3", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^1.1.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.1" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz", + "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.4", + "caniuse-lite": "^1.0.30001799", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.40.7", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.7.tgz", + "integrity": "sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.12.tgz", + "integrity": "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.40.6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "solid-js": "^1.9.12" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", + "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/birpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz", + "integrity": "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bonjour-service": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.4.3.tgz", + "integrity": "sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", + "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001805", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "license": "MIT" + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", + "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, + "node_modules/compatx": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/compatx/-/compatx-0.2.0.tgz", + "integrity": "sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-es": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.1.tgz", + "integrity": "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==", + "license": "MIT" + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/croner": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", + "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", + "funding": [ + { + "type": "other", + "url": "https://paypal.me/hexagonpp" + }, + { + "type": "github", + "url": "https://github.com/sponsors/hexagon" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz", + "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==", + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.9.tgz", + "integrity": "sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==", + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^7.0.17", + "lilconfig": "^3.1.3" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/cssnano-preset-default": { + "version": "7.0.17", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.17.tgz", + "integrity": "sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.3", + "postcss-calc": "^10.1.1", + "postcss-colormin": "^7.0.10", + "postcss-convert-values": "^7.0.12", + "postcss-discard-comments": "^7.0.8", + "postcss-discard-duplicates": "^7.0.4", + "postcss-discard-empty": "^7.0.3", + "postcss-discard-overridden": "^7.0.3", + "postcss-merge-longhand": "^7.0.7", + "postcss-merge-rules": "^7.0.11", + "postcss-minify-font-values": "^7.0.3", + "postcss-minify-gradients": "^7.0.5", + "postcss-minify-params": "^7.0.9", + "postcss-minify-selectors": "^7.1.2", + "postcss-normalize-charset": "^7.0.3", + "postcss-normalize-display-values": "^7.0.3", + "postcss-normalize-positions": "^7.0.4", + "postcss-normalize-repeat-style": "^7.0.4", + "postcss-normalize-string": "^7.0.3", + "postcss-normalize-timing-functions": "^7.0.3", + "postcss-normalize-unicode": "^7.0.9", + "postcss-normalize-url": "^7.0.3", + "postcss-normalize-whitespace": "^7.0.3", + "postcss-ordered-values": "^7.0.4", + "postcss-reduce-initial": "^7.0.9", + "postcss-reduce-transforms": "^7.0.3", + "postcss-svgo": "^7.1.3", + "postcss-unique-selectors": "^7.0.7" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/cssnano-utils": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.3.tgz", + "integrity": "sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==", + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/db0": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/db0/-/db0-0.3.4.tgz", + "integrity": "sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==", + "license": "MIT", + "peerDependencies": { + "@electric-sql/pglite": "*", + "@libsql/client": "*", + "better-sqlite3": "*", + "drizzle-orm": "*", + "mysql2": "*", + "sqlite3": "*" + }, + "peerDependenciesMeta": { + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "drizzle-orm": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz", + "integrity": "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==", + "license": "MIT", + "dependencies": { + "type-fest": "^5.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.389", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", + "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", + "license": "ISC" + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.2.tgz", + "integrity": "sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/errx": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/errx/-/errx-0.1.0.tgz", + "integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz", + "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", + "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/exsolve": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.0.tgz", + "integrity": "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-npm-meta": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/fast-npm-meta/-/fast-npm-meta-1.5.1.tgz", + "integrity": "sha512-tWhw7z4jFuQgZB9tbQyUh5BY9nNd/wimM+fBLfmmJjakkJDNvbJKm0nQ5ruPKC0us1HGg7L6iBk1fxpSzcgSaA==", + "license": "MIT", + "bin": { + "fast-npm-meta": "dist/cli.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuse.js": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.4.2.tgz", + "integrity": "sha512-LVbzjD4WA6UP5B1UnP8wuaXJiLnqMdM/E4fiJXTJ5haJ5b/MBNsK29h2fm6swEoQaVQjvYFWKLE2RanyZIoRVQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/krisk" + } + }, + "node_modules/fzf": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", + "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", + "license": "BSD-3-Clause" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "license": "MIT" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.0.tgz", + "integrity": "sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==", + "license": "MIT", + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regex.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/globby": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.1.tgz", + "integrity": "sha512-JmsqJalahxxgW8V2ecSQ2G7UjPlI9cpKdrkG9KoNiXhd/YslXOTEB0cViENWUznuovIuNT+FkMbraDGjr4FCUg==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gzip-size": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz", + "integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/h3/node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hono": { + "version": "4.12.29", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.29.tgz", + "integrity": "sha512-1hNiRjawYrLq/4m3DQQjPGFg0VZkk4RjQJDff/excI6Dm9BiL75qxGrd7/c6YOxPdq6AscP3LiXhQ6fKFC1Waw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.7", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", + "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz", + "integrity": "sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-shutdown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", + "integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==", + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/httpxy": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.5.5.tgz", + "integrity": "sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==", + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/image-meta": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/image-meta/-/image-meta-0.2.2.tgz", + "integrity": "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==", + "license": "MIT" + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/impound": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/impound/-/impound-1.1.5.tgz", + "integrity": "sha512-5AUn+QE0UofqNHu5f2Skf6Svukdg4ehOIq8O0EtqIx4jta0CDZYBPqpIHt0zrlUTiFVYlLpeH39DoikXBjPKpA==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "es-module-lexer": "^2.0.0", + "pathe": "^2.0.3", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1" + } + }, + "node_modules/impound/node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ioredis": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", + "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "license": "MIT" + }, + "node_modules/is-network-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/knitwork": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/knitwork/-/knitwork-1.3.0.tgz", + "integrity": "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==", + "license": "MIT" + }, + "node_modules/launch-editor": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", + "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.4" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/listhen": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.10.0.tgz", + "integrity": "sha512-kfz4C0OrC6IpaVMtYDJtf6PFjurxe9NBBoDAh/o2p587INryFOO4DQ9OetbCdDrWFt1m1CJKvYrzkGsuPHw8nQ==", + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.5.6", + "@parcel/watcher-wasm": "^2.5.6", + "citty": "^0.2.2", + "consola": "^3.4.2", + "crossws": ">=0.2.0 <0.5.0", + "defu": "^6.1.7", + "get-port-please": "^3.2.0", + "h3": "^1.15.11", + "http-shutdown": "^1.2.2", + "jiti": "^2.6.1", + "mlly": "^1.8.2", + "node-forge": "^1.4.0", + "pathe": "^2.0.3", + "std-env": "^4.1.0", + "tinyclip": "^0.1.12", + "ufo": "^1.6.4", + "untun": "^0.1.3", + "uqr": "^0.1.3" + }, + "bin": { + "listen": "bin/listhen.mjs", + "listhen": "bin/listhen.mjs" + } + }, + "node_modules/listhen/node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-regexp": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.10.0.tgz", + "integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==", + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12", + "mlly": "^1.7.2", + "regexp-tree": "^0.1.27", + "type-level-regexp": "~0.1.17", + "ufo": "^1.5.4", + "unplugin": "^2.0.0" + } + }, + "node_modules/magic-regexp/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-fetch-happen": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", + "integrity": "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.64.0.tgz", + "integrity": "sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.64.0", + "@jsonjoy.com/fs-fsa": "4.64.0", + "@jsonjoy.com/fs-node": "4.64.0", + "@jsonjoy.com/fs-node-builtins": "4.64.0", + "@jsonjoy.com/fs-node-to-fsa": "4.64.0", + "@jsonjoy.com/fs-node-utils": "4.64.0", + "@jsonjoy.com/fs-print": "4.64.0", + "@jsonjoy.com/fs-snapshot": "4.64.0", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.0.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/merge-anything": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", + "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/mocked-exports": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/mocked-exports/-/mocked-exports-0.1.1.tgz", + "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==", + "license": "MIT" + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz", + "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==", + "dev": true, + "license": "MIT", + "optional": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanotar": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.2.1.tgz", + "integrity": "sha512-MUrzzDUcIOPbv7ubhDV/L4CIfVTATd9XhDE2ixFeCrM5yp9AlzUpn91JrnN0HD6hksdxvz9IW9aKANz0Bta0GA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/next": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", + "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.10", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.10", + "@next/swc-darwin-x64": "16.2.10", + "@next/swc-linux-arm64-gnu": "16.2.10", + "@next/swc-linux-arm64-musl": "16.2.10", + "@next/swc-linux-x64-gnu": "16.2.10", + "@next/swc-linux-x64-musl": "16.2.10", + "@next/swc-win32-arm64-msvc": "16.2.10", + "@next/swc-win32-x64-msvc": "16.2.10", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/next/node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/nitropack": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.13.4.tgz", + "integrity": "sha512-tX7bT6zxNeMwkc6hxHiZeUoTOjVrcjoh1Z3cmxOlodIqjl4HISgqfGOmkWSayky3Nv9Z5+KQH52F8nmXJY5AAA==", + "license": "MIT", + "dependencies": { + "@cloudflare/kv-asset-handler": "^0.4.2", + "@rollup/plugin-alias": "^6.0.0", + "@rollup/plugin-commonjs": "^29.0.2", + "@rollup/plugin-inject": "^5.0.5", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^6.0.3", + "@rollup/plugin-terser": "^1.0.0", + "@vercel/nft": "^1.5.0", + "archiver": "^7.0.1", + "c12": "^3.3.4", + "chokidar": "^5.0.0", + "citty": "^0.2.2", + "compatx": "^0.2.0", + "confbox": "^0.2.4", + "consola": "^3.4.2", + "cookie-es": "^2.0.1", + "croner": "^10.0.1", + "crossws": "^0.3.5", + "db0": "^0.3.4", + "defu": "^6.1.7", + "destr": "^2.0.5", + "dot-prop": "^10.1.0", + "esbuild": "^0.28.0", + "escape-string-regexp": "^5.0.0", + "etag": "^1.8.1", + "exsolve": "^1.0.8", + "globby": "^16.2.0", + "gzip-size": "^7.0.0", + "h3": "^1.15.11", + "hookable": "^5.5.3", + "httpxy": "^0.5.1", + "ioredis": "^5.10.1", + "jiti": "^2.6.1", + "klona": "^2.0.6", + "knitwork": "^1.3.0", + "listhen": "^1.9.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mime": "^4.1.0", + "mlly": "^1.8.2", + "node-fetch-native": "^1.6.7", + "node-mock-http": "^1.0.4", + "ofetch": "^1.5.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.1", + "pretty-bytes": "^7.1.0", + "radix3": "^1.1.2", + "rollup": "^4.60.2", + "rollup-plugin-visualizer": "^7.0.1", + "scule": "^1.3.0", + "semver": "^7.7.4", + "serve-placeholder": "^2.0.2", + "serve-static": "^2.2.1", + "source-map": "^0.7.6", + "std-env": "^4.1.0", + "ufo": "^1.6.4", + "ultrahtml": "^1.6.0", + "uncrypto": "^0.1.3", + "unctx": "^2.5.0", + "unenv": "2.0.0-rc.24", + "unimport": "^6.2.0", + "unplugin-utils": "^0.3.1", + "unstorage": "^1.17.5", + "untyped": "^2.0.0", + "unwasm": "^0.5.3", + "youch": "^4.1.1", + "youch-core": "^0.3.3" + }, + "bin": { + "nitro": "dist/cli/index.mjs", + "nitropack": "dist/cli/index.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "xml2js": "^0.6.2" + }, + "peerDependenciesMeta": { + "xml2js": { + "optional": true + } + } + }, + "node_modules/nitropack/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nitropack/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/nitropack/node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nitropack/node_modules/rollup-plugin-visualizer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-7.0.1.tgz", + "integrity": "sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==", + "license": "MIT", + "dependencies": { + "open": "^11.0.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^18.0.0" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/nitropack/node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "license": "MIT" + }, + "node_modules/nitropack/node_modules/unimport": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-6.3.0.tgz", + "integrity": "sha512-M+Dxk5W9WRd+8j56W9tp8lGW/dmMc7g5zj7BWQnEjKQhryBstqsi1V0izb0zHwSkEN8cSYV7K75/bykairV2tA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "pathe": "^2.0.3", + "picomatch": "^4.0.4", + "pkg-types": "^2.3.1", + "scule": "^1.3.0", + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.16", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "oxc-parser": "*", + "rolldown": "^1.0.0" + }, + "peerDependenciesMeta": { + "oxc-parser": { + "optional": true + }, + "rolldown": { + "optional": true + } + } + }, + "node_modules/nitropack/node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^5.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-install-checks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.4.tgz", + "integrity": "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^4.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^15.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nuxt": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.3.1.tgz", + "integrity": "sha512-bl+0rFcT5Ax16aiWFBFPyWcsTob19NTZaDL5P6t0MQdK63AtgS6fN6fwvwdbXtnTk6/YdCzlmuLzXhSM22h0OA==", + "license": "MIT", + "dependencies": { + "@dxup/nuxt": "^0.3.2", + "@nuxt/cli": "^3.33.0", + "@nuxt/devtools": "^3.1.1", + "@nuxt/kit": "4.3.1", + "@nuxt/nitro-server": "4.3.1", + "@nuxt/schema": "4.3.1", + "@nuxt/telemetry": "^2.7.0", + "@nuxt/vite-builder": "4.3.1", + "@unhead/vue": "^2.1.3", + "@vue/shared": "^3.5.27", + "c12": "^3.3.3", + "chokidar": "^5.0.0", + "compatx": "^0.2.0", + "consola": "^3.4.2", + "cookie-es": "^2.0.0", + "defu": "^6.1.4", + "destr": "^2.0.5", + "devalue": "^5.6.2", + "errx": "^0.1.0", + "escape-string-regexp": "^5.0.0", + "exsolve": "^1.0.8", + "h3": "^1.15.5", + "hookable": "^5.5.3", + "ignore": "^7.0.5", + "impound": "^1.0.0", + "jiti": "^2.6.1", + "klona": "^2.0.6", + "knitwork": "^1.3.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "nanotar": "^0.2.0", + "nypm": "^0.6.5", + "ofetch": "^1.5.1", + "ohash": "^2.0.11", + "on-change": "^6.0.2", + "oxc-minify": "^0.112.0", + "oxc-parser": "^0.112.0", + "oxc-transform": "^0.112.0", + "oxc-walker": "^0.7.0", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rou3": "^0.7.12", + "scule": "^1.3.0", + "semver": "^7.7.4", + "std-env": "^3.10.0", + "tinyglobby": "^0.2.15", + "ufo": "^1.6.3", + "ultrahtml": "^1.6.0", + "uncrypto": "^0.1.3", + "unctx": "^2.5.0", + "unimport": "^5.6.0", + "unplugin": "^3.0.0", + "unplugin-vue-router": "^0.19.2", + "untyped": "^2.0.0", + "vue": "^3.5.27", + "vue-router": "^4.6.4" + }, + "bin": { + "nuxi": "bin/nuxt.mjs", + "nuxt": "bin/nuxt.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@parcel/watcher": "^2.1.0", + "@types/node": ">=18.12.0" + }, + "peerDependenciesMeta": { + "@parcel/watcher": { + "optional": true + }, + "@types/node": { + "optional": true + } + } + }, + "node_modules/nuxt/node_modules/@nuxt/schema": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-4.3.1.tgz", + "integrity": "sha512-S+wHJdYDuyk9I43Ej27y5BeWMZgi7R/UVql3b3qtT35d0fbpXW7fUenzhLRCCDC6O10sjguc6fcMcR9sMKvV8g==", + "license": "MIT", + "dependencies": { + "@vue/shared": "^3.5.27", + "defu": "^6.1.4", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "std-env": "^3.10.0" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/nuxt/node_modules/unplugin": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", + "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.4", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/nypm": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.8.tgz", + "integrity": "sha512-Q9K4Diu6l5u6xJQogeFSs/zKtyMSgFKFtRQV+tHP4kL7KPm2grpBU0dFIwFaXwNxN0MtfKWc43VpCugAa+LPsw==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.2", + "pathe": "^2.0.3", + "tinyexec": "^1.2.4" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/on-change": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/on-change/-/on-change-6.0.2.tgz", + "integrity": "sha512-08+12qcOVEA0fS9g/VxKS27HaT94nRutUT77J2dr8zv/unzXopvhBuF8tNLWsoLQ5IgrQ6eptGeGqUYat82U1w==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/on-change?sponsor=1" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ordered-binary": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", + "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/oxc-minify": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/oxc-minify/-/oxc-minify-0.112.0.tgz", + "integrity": "sha512-rkVSeeIRSt+RYI9uX6xonBpLUpvZyegxIg0UL87ev7YAfUqp7IIZlRjkgQN5Us1lyXD//TOo0Dcuuro/TYOWoQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-minify/binding-android-arm-eabi": "0.112.0", + "@oxc-minify/binding-android-arm64": "0.112.0", + "@oxc-minify/binding-darwin-arm64": "0.112.0", + "@oxc-minify/binding-darwin-x64": "0.112.0", + "@oxc-minify/binding-freebsd-x64": "0.112.0", + "@oxc-minify/binding-linux-arm-gnueabihf": "0.112.0", + "@oxc-minify/binding-linux-arm-musleabihf": "0.112.0", + "@oxc-minify/binding-linux-arm64-gnu": "0.112.0", + "@oxc-minify/binding-linux-arm64-musl": "0.112.0", + "@oxc-minify/binding-linux-ppc64-gnu": "0.112.0", + "@oxc-minify/binding-linux-riscv64-gnu": "0.112.0", + "@oxc-minify/binding-linux-riscv64-musl": "0.112.0", + "@oxc-minify/binding-linux-s390x-gnu": "0.112.0", + "@oxc-minify/binding-linux-x64-gnu": "0.112.0", + "@oxc-minify/binding-linux-x64-musl": "0.112.0", + "@oxc-minify/binding-openharmony-arm64": "0.112.0", + "@oxc-minify/binding-wasm32-wasi": "0.112.0", + "@oxc-minify/binding-win32-arm64-msvc": "0.112.0", + "@oxc-minify/binding-win32-ia32-msvc": "0.112.0", + "@oxc-minify/binding-win32-x64-msvc": "0.112.0" + } + }, + "node_modules/oxc-parser": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.112.0.tgz", + "integrity": "sha512-7rQ3QdJwobMQLMZwQaPuPYMEF2fDRZwf51lZ//V+bA37nejjKW5ifMHbbCwvA889Y4RLhT+/wLJpPRhAoBaZYw==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.112.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.112.0", + "@oxc-parser/binding-android-arm64": "0.112.0", + "@oxc-parser/binding-darwin-arm64": "0.112.0", + "@oxc-parser/binding-darwin-x64": "0.112.0", + "@oxc-parser/binding-freebsd-x64": "0.112.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.112.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.112.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.112.0", + "@oxc-parser/binding-linux-arm64-musl": "0.112.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.112.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.112.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.112.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.112.0", + "@oxc-parser/binding-linux-x64-gnu": "0.112.0", + "@oxc-parser/binding-linux-x64-musl": "0.112.0", + "@oxc-parser/binding-openharmony-arm64": "0.112.0", + "@oxc-parser/binding-wasm32-wasi": "0.112.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.112.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.112.0", + "@oxc-parser/binding-win32-x64-msvc": "0.112.0" + } + }, + "node_modules/oxc-transform": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/oxc-transform/-/oxc-transform-0.112.0.tgz", + "integrity": "sha512-cIRRvZgrHfsAHrkt8LWdAX4+Do8R0MzQSfeo9yzErzHeYiuyNiP4PCTPbOy/wBXL4MYzt3ebrBa5jt3akQkKAg==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-transform/binding-android-arm-eabi": "0.112.0", + "@oxc-transform/binding-android-arm64": "0.112.0", + "@oxc-transform/binding-darwin-arm64": "0.112.0", + "@oxc-transform/binding-darwin-x64": "0.112.0", + "@oxc-transform/binding-freebsd-x64": "0.112.0", + "@oxc-transform/binding-linux-arm-gnueabihf": "0.112.0", + "@oxc-transform/binding-linux-arm-musleabihf": "0.112.0", + "@oxc-transform/binding-linux-arm64-gnu": "0.112.0", + "@oxc-transform/binding-linux-arm64-musl": "0.112.0", + "@oxc-transform/binding-linux-ppc64-gnu": "0.112.0", + "@oxc-transform/binding-linux-riscv64-gnu": "0.112.0", + "@oxc-transform/binding-linux-riscv64-musl": "0.112.0", + "@oxc-transform/binding-linux-s390x-gnu": "0.112.0", + "@oxc-transform/binding-linux-x64-gnu": "0.112.0", + "@oxc-transform/binding-linux-x64-musl": "0.112.0", + "@oxc-transform/binding-openharmony-arm64": "0.112.0", + "@oxc-transform/binding-wasm32-wasi": "0.112.0", + "@oxc-transform/binding-win32-arm64-msvc": "0.112.0", + "@oxc-transform/binding-win32-ia32-msvc": "0.112.0", + "@oxc-transform/binding-win32-x64-msvc": "0.112.0" + } + }, + "node_modules/oxc-walker": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-0.7.0.tgz", + "integrity": "sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==", + "license": "MIT", + "dependencies": { + "magic-regexp": "^0.10.0" + }, + "peerDependencies": { + "oxc-parser": ">=0.98.0" + } + }, + "node_modules/p-limit": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz", + "integrity": "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.5.tgz", + "integrity": "sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.1.tgz", + "integrity": "sha512-POWdiIPmsUPGwb4FeQ4OBg46aqmcInSWe45CKDsGHiOBiVQM9chqfQTuqhuTzcg2Vz9faTI65at0KkVyVEiCHw==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz", + "integrity": "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==", + "license": "MIT" + }, + "node_modules/pacote": { + "version": "21.5.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.1.tgz", + "integrity": "sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", + "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.2.0.tgz", + "integrity": "sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.4" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/postcss": { + "version": "8.5.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.17.tgz", + "integrity": "sha512-J7EF+8X+CzRPaJPOv9Ck2wNWJvGnnl3PcNPAdGg6GTLjyVpyQ0yATMSXRFRV01BviT/9Gwuc3rjEyJbDJG9a4w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", + "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/postcss-colormin": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.10.tgz", + "integrity": "sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==", + "license": "MIT", + "dependencies": { + "@colordx/core": "^5.4.3", + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-convert-values": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.12.tgz", + "integrity": "sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-comments": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.8.tgz", + "integrity": "sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.4.tgz", + "integrity": "sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==", + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-empty": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.3.tgz", + "integrity": "sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==", + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.3.tgz", + "integrity": "sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==", + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.7.tgz", + "integrity": "sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.11" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-merge-rules": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.11.tgz", + "integrity": "sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.3", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.3.tgz", + "integrity": "sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.5.tgz", + "integrity": "sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==", + "license": "MIT", + "dependencies": { + "@colordx/core": "^5.4.3", + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-params": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.9.tgz", + "integrity": "sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.1.2.tgz", + "integrity": "sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-api": "^3.0.0", + "cssesc": "^3.0.0", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.3.tgz", + "integrity": "sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==", + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.3.tgz", + "integrity": "sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.4.tgz", + "integrity": "sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.4.tgz", + "integrity": "sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-string": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.3.tgz", + "integrity": "sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.3.tgz", + "integrity": "sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.9.tgz", + "integrity": "sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-url": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.3.tgz", + "integrity": "sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.3.tgz", + "integrity": "sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-ordered-values": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.4.tgz", + "integrity": "sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==", + "license": "MIT", + "dependencies": { + "cssnano-utils": "^5.0.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.9.tgz", + "integrity": "sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.3.tgz", + "integrity": "sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.3.tgz", + "integrity": "sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^4.0.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.7.tgz", + "integrity": "sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prettier": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", + "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-7.1.0.tgz", + "integrity": "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc9": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz", + "integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.6", + "destr": "^2.0.5" + } + }, + "node_modules/react": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.5" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-visualizer": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.11.tgz", + "integrity": "sha512-TBwVHVY7buHjIKVLqr9scTVFwqZqMXINcCphPwIWKPDCOBIa+jCQfafvbjRJDZgXdq/A996Dy6yGJ/+/NtAXDQ==", + "license": "MIT", + "dependencies": { + "open": "^8.0.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "rolldown": "1.x || ^1.0.0-beta", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/rou3": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.7.12.tgz", + "integrity": "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==", + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serialize-javascript": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz", + "integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/seroval": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.5.tgz", + "integrity": "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.5.tgz", + "integrity": "sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-placeholder": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/serve-placeholder/-/serve-placeholder-2.0.2.tgz", + "integrity": "sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", + "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shiki": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz", + "integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.3.1", + "@shikijs/engine-javascript": "4.3.1", + "@shikijs/engine-oniguruma": "4.3.1", + "@shikijs/langs": "4.3.1", + "@shikijs/themes": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.1.tgz", + "integrity": "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.1", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.1", + "@sigstore/tuf": "^4.0.2", + "@sigstore/verify": "^3.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/simple-git": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz", + "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==", + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "@simple-git/args-pathspec": "^1.0.3", + "@simple-git/argv-parser": "^1.1.0", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/solid-js": { + "version": "1.9.14", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.14.tgz", + "integrity": "sha512-sAEXC0Kk0S1EDg+8ysEWJDbYhA3RRoEjwuySUGlKIemeo0I5YZfOyumNjNs9Sv3y2nmhD+0rW66ag2HsMuQiGQ==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.5.4", + "seroval-plugins": "~1.5.4" + } + }, + "node_modules/solid-refresh": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", + "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.23.6", + "@babel/helper-module-imports": "^7.22.15", + "@babel/types": "^7.23.6" + }, + "peerDependencies": { + "solid-js": "^1.3" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/srvx": { + "version": "0.11.22", + "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.22.tgz", + "integrity": "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", + "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "license": "MIT" + }, + "node_modules/structured-clone-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/structured-clone-es/-/structured-clone-es-2.0.0.tgz", + "integrity": "sha512-5UuAHmBLXYPCl22xWJrFuGmIhBKQzxISPVz6E7nmTmTcAOpUzlbjKJsRrCE4vADmMQ0dzeCnlWn9XufnAGf76Q==", + "license": "ISC" + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/stylehacks": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.11.tgz", + "integrity": "sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "postcss-selector-parser": "^7.1.1" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.5.13" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", + "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.12", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.2.tgz", + "integrity": "sha512-GoS4XJdGswlq0rIT1vtFLzJY1bvHtY37McY9H9Gkm1Ggw/ICdZYn8J/Z8Yi0BEL0i3R4+jtaWVePjyppMlij/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "^0.2.0", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte-check/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/svelte-check/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/svelte/node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.5.20", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.20.tgz", + "integrity": "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/terser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/thingies": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", + "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-loader": { + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.2.tgz", + "integrity": "sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "picomatch": "^4.0.0", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "loader-utils": "*", + "typescript": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "loader-utils": { + "optional": true + } + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/type-fest": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-level-regexp": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz", + "integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==", + "license": "MIT" + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/unctx": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", + "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21", + "unplugin": "^2.3.11" + } + }, + "node_modules/unctx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/undici": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", + "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3" + } + }, + "node_modules/unhead": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.15.tgz", + "integrity": "sha512-MCt5T90mCWyr3Z6pUCdM9lVRXoMoVBlL7z7U4CYVIiaDiuzad/UCfLuMqz5MeNmpZUgoBCQnrucJimU7EZR+XA==", + "license": "MIT", + "dependencies": { + "hookable": "^6.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + } + }, + "node_modules/unhead/node_modules/hookable": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz", + "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==", + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", + "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unimport": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.7.0.tgz", + "integrity": "sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", + "scule": "^1.3.0", + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unimport/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-vue-router": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.19.2.tgz", + "integrity": "sha512-u5dgLBarxE5cyDK/hzJGfpCTLIAyiTXGlo85COuD4Nssj6G7NxS+i9mhCWz/1p/ud1eMwdcUbTXehQe41jYZUA==", + "deprecated": "Merged into vuejs/router. Migrate: https://router.vuejs.org/guide/migration/v4-to-v5.html", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.5", + "@vue-macros/common": "^3.1.1", + "@vue/language-core": "^3.2.1", + "ast-walker-scope": "^0.8.3", + "chokidar": "^5.0.0", + "json5": "^2.2.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "muggle-string": "^0.4.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "scule": "^1.3.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1", + "yaml": "^2.8.2" + }, + "peerDependencies": { + "@vue/compiler-sfc": "^3.5.17", + "vue-router": "^4.6.0" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/untun": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz", + "integrity": "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.5", + "consola": "^3.2.3", + "pathe": "^1.1.1" + }, + "bin": { + "untun": "bin/untun.mjs" + } + }, + "node_modules/untun/node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/untun/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "license": "MIT" + }, + "node_modules/untyped": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/untyped/-/untyped-2.0.0.tgz", + "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "defu": "^6.1.4", + "jiti": "^2.4.2", + "knitwork": "^1.2.0", + "scule": "^1.3.0" + }, + "bin": { + "untyped": "dist/cli.mjs" + } + }, + "node_modules/untyped/node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/unwasm": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/unwasm/-/unwasm-0.5.3.tgz", + "integrity": "sha512-keBgTSfp3r6+s9ZcSma+0chwxQdmLbB5+dAD9vjtB21UTMYuKAxHXCU1K2CbCtnP09EaWeRvACnXk0EJtUx+hw==", + "license": "MIT", + "dependencies": { + "exsolve": "^1.0.8", + "knitwork": "^1.3.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uqr": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz", + "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", + "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.16", + "rolldown": "~1.1.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-2.0.0.tgz", + "integrity": "sha512-yKwbTwdHKSD2k/aGqyWpPHepo45OQc8lH3/6IfT4ZqeKE26ooKvi4WIEKzqWav8v+9Is8u1k8q54hvOmqASazA==", + "license": "MIT", + "dependencies": { + "birpc": "^4.0.0", + "vite-hot-client": "^2.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 || ^8.0.0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.2.0.tgz", + "integrity": "sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0" + } + }, + "node_modules/vite-node": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.3.0.tgz", + "integrity": "sha512-8f20COPYJujc3OKPX6OuyBy3ZIv2det4eRRU4GY1y2MjbeGSUmPjedxg1b72KnTagCofwvZ65ThzjxDW2AtQFQ==", + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "es-module-lexer": "^2.0.0", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "vite": "^7.3.1" + }, + "bin": { + "vite-node": "dist/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://opencollective.com/antfu" + } + }, + "node_modules/vite-node/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-checker": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.12.0.tgz", + "integrity": "sha512-CmdZdDOGss7kdQwv73UyVgLPv0FVYe5czAgnmRX2oKljgEvSrODGuClaV3PDR2+3ou7N/OKGauDDBjy2MB07Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "chokidar": "^4.0.3", + "npm-run-path": "^6.0.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.3", + "tiny-invariant": "^1.3.3", + "tinyglobby": "^0.2.15", + "vscode-uri": "^3.1.0" + }, + "engines": { + "node": ">=16.11" + }, + "peerDependencies": { + "@biomejs/biome": ">=1.7", + "eslint": ">=9.39.1", + "meow": "^13.2.0", + "optionator": "^0.9.4", + "oxlint": ">=1", + "stylelint": ">=16", + "typescript": "*", + "vite": ">=5.4.21", + "vls": "*", + "vti": "*", + "vue-tsc": "~2.2.10 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@biomejs/biome": { + "optional": true + }, + "eslint": { + "optional": true + }, + "meow": { + "optional": true + }, + "optionator": { + "optional": true + }, + "oxlint": { + "optional": true + }, + "stylelint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vls": { + "optional": true + }, + "vti": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/vite-plugin-checker/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vite-plugin-checker/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-checker/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-checker/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vite-plugin-checker/node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.4.1.tgz", + "integrity": "sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==", + "license": "MIT", + "dependencies": { + "ansis": "^4.3.0", + "error-stack-parser-es": "^1.0.5", + "obug": "^2.1.1", + "ohash": "^2.0.11", + "open": "^11.0.0", + "perfect-debounce": "^2.1.0", + "sirv": "^3.0.2", + "unplugin-utils": "^0.3.1", + "vite-dev-rpc": "^2.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect/node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-solid": { + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.12.tgz", + "integrity": "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.3", + "@types/babel__core": "^7.20.4", + "babel-preset-solid": "^1.8.4", + "merge-anything": "^5.1.7", + "solid-refresh": "^0.6.3", + "vitefu": "^1.0.4" + }, + "peerDependencies": { + "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", + "solid-js": "^1.7.2", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@testing-library/jest-dom": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vue-tracer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-tracer/-/vite-plugin-vue-tracer-1.4.0.tgz", + "integrity": "sha512-0tQCjCqZWVSK6UeRW9S4ABbf47lKQ68zvrT2FNvZmiL+alDydCVyH/T3Jlfbdc3T3C2Iuyyl5aVsMbF8IQIoxA==", + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "exsolve": "^1.0.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3", + "source-map-js": "^1.2.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "vue": "^3.5.0" + } + }, + "node_modules/vite-plugin-vue-tracer/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", + "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", + "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.1", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.39.tgz", + "integrity": "sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-sfc": "3.5.39", + "@vue/runtime-dom": "3.5.39", + "@vue/server-renderer": "3.5.39", + "@vue/shared": "3.5.39" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-bundle-renderer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-2.3.1.tgz", + "integrity": "sha512-7F4LNMopUw5RgYWo4zCmVUHCc6aQRC6dCKHUYkM/n+fux4AUGdL1x6m5A515WWyFysRRN7cx3hBzVqoisfRfzw==", + "license": "MIT", + "dependencies": { + "ufo": "^1.6.4" + } + }, + "node_modules/vue-devtools-stub": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz", + "integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==", + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.7.tgz", + "integrity": "sha512-+C+rgD49wAQ5bUTl2sp5a8Bzg4YoldMNXM+g7CFe604MYcQ8PrZPMQhIjJSzKXtPBCa+C5ayMipqjbA7splekQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.7" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.108.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz", + "integrity": "sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.22.2", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.6.1", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.2.1.tgz", + "integrity": "sha512-YwSGbcZdfz12DM8JIseVPr3oBb09IgVCVc4vY3oDvZnI/mALTGPAP1QiqOi4/bBLSJrRHaqDIXeHcNA0+G38aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^1.1.0", + "commander": "^14.0.3", + "cross-spawn": "^7.0.6", + "envinfo": "^7.21.0", + "import-local": "^3.2.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "js-yaml": "^4.0.0 || ^5.0.0", + "json5": "^2.2.3", + "toml": "^3.0.0 || ^4.0.0", + "webpack": "^5.101.0", + "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", + "webpack-dev-server": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "js-yaml": { + "optional": true + }, + "json5": { + "optional": true + }, + "toml": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.5.tgz", + "integrity": "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.43.1", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz", + "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/express-serve-static-core": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.21.2", + "graceful-fs": "^4.2.6", + "http-proxy-middleware": "^2.0.9", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/webpack-dev-server/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-server/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-dev-server/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/webpack-dev-server/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/webpack-dev-server/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/webpack-dev-server/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/webpack/node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz", + "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/youch": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.1.tgz", + "integrity": "sha512-mxW3qiSnl+GRxXsaUMzv2Mbada1Y8CDltET9UxejDQe6DBYlSekghl5U5K0ReAikcHDi0G1vKZEmmo/NWAGKLA==", + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.6", + "@poppinss/dumper": "^0.7.0", + "@speed-highlight/core": "^1.2.14", + "cookie-es": "^3.0.1", + "youch-core": "^0.3.3" + } + }, + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "license": "MIT", + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" + } + }, + "node_modules/youch/node_modules/cookie-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", + "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", + "license": "MIT" + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "license": "MIT" + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zod": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.2.tgz", + "integrity": "sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zone.js": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.2.tgz", + "integrity": "sha512-Eky7p2Z1Ig3NnbfodSPoARCjKBSTFMnE/ACsP1L/XJEfY4SdOFce19BsUCWVwL6K5ABZFy5J3bjcMWffX+YM3Q==", + "license": "MIT" + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/integration/package.json b/integration/package.json new file mode 100644 index 0000000..f71dc95 --- /dev/null +++ b/integration/package.json @@ -0,0 +1,20 @@ +{ + "name": "@svg-sprites-fixtures/integration", + "private": true, + "version": "0.0.0", + "type": "module", + "workspaces": [ + "apps/*" + ], + "scripts": { + "generate": "node scripts/run-workspaces.mjs sprites", + "typecheck": "node scripts/run-workspaces.mjs typecheck", + "build": "node scripts/run-workspaces.mjs build", + "test:e2e": "playwright test", + "verify": "npm run generate && npm run typecheck && npm run build && npm run test:e2e" + }, + "devDependencies": { + "@playwright/test": "1.61.1", + "pngjs": "7.0.0" + } +} diff --git a/integration/playwright.config.ts b/integration/playwright.config.ts new file mode 100644 index 0000000..1d6811c --- /dev/null +++ b/integration/playwright.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: './tests', + fullyParallel: false, + workers: 1, + timeout: 120_000, + expect: { + timeout: 10_000, + }, + reporter: process.env.CI ? 'github' : 'list', + use: { + browserName: 'chromium', + headless: true, + }, +}) diff --git a/integration/scripts/apps.mjs b/integration/scripts/apps.mjs new file mode 100644 index 0000000..7f9e064 --- /dev/null +++ b/integration/scripts/apps.mjs @@ -0,0 +1,8 @@ +export const apps = [ + { id: 'react-vite', kind: 'static', output: 'dist' }, + { id: 'react-webpack', kind: 'static', output: 'dist' }, + { id: 'next-app-turbopack', kind: 'next' }, + { id: 'next-app-webpack', kind: 'next' }, + { id: 'next-pages-turbopack', kind: 'next' }, + { id: 'next-pages-webpack', kind: 'next' }, +] diff --git a/integration/scripts/run-workspaces.mjs b/integration/scripts/run-workspaces.mjs new file mode 100644 index 0000000..d2549df --- /dev/null +++ b/integration/scripts/run-workspaces.mjs @@ -0,0 +1,31 @@ +import { spawn } from 'node:child_process' +import process from 'node:process' + +import { apps } from './apps.mjs' + +const script = process.argv[2] +const requestedApps = new Set(process.argv.slice(3)) + +if (!script) { + throw new Error('Usage: node scripts/run-workspaces.mjs ', - ) - fs.writeFileSync( - path.join(rootDir, 'main.js'), - [ - "import spriteUrl from './sprite.svg?no-inline'", - "document.querySelector('#root').dataset.spriteUrl = spriteUrl", - '', - ].join('\n'), - ) - fs.writeFileSync(path.join(rootDir, 'sprite.svg'), TINY_SPRITE) - - await viteBuild({ - root: rootDir, - logLevel: 'silent', - build: { - outDir: outputDir, - }, - }) - - const svgFiles = getOutputFiles(outputDir, '.svg') - const jsFiles = getOutputFiles(outputDir, '.js') - const javascript = jsFiles - .map((filePath) => fs.readFileSync(path.join(outputDir, filePath), 'utf-8')) - .join('\n') - - assert.equal(svgFiles.length, 1) - assert.match(svgFiles[0], /sprite-[A-Za-z0-9_-]+\.svg$/) - assert.doesNotMatch(javascript, /data:image\/svg\+xml/) -}) - -test('Webpack target emits new URL sprite through Asset Modules', async () => { - const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-webpack-target-')) - const outputDir = path.join(rootDir, 'dist') - - fs.writeFileSync( - path.join(rootDir, 'index.js'), - [ - "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", - 'globalThis.__SPRITE_URL__ = spriteUrl', - '', - ].join('\n'), - ) - fs.writeFileSync(path.join(rootDir, 'sprite.svg'), TINY_SPRITE) - - await new Promise((resolve, reject) => { - webpack({ - context: rootDir, - entry: './index.js', - mode: 'production', - output: { - assetModuleFilename: 'assets/[name]-[contenthash][ext]', - filename: 'bundle.js', - path: outputDir, - }, - }, (error, stats) => { - if (error) { - reject(error) - return - } - - if (!stats || stats.hasErrors()) { - const errors = stats?.toJson({ all: false, errors: true }).errors ?? [] - reject(new Error(errors.map((item) => item.message).join('\n'))) - return - } - - resolve() - }) - }) - - const svgFiles = getOutputFiles(outputDir, '.svg') - const javascript = fs.readFileSync(path.join(outputDir, 'bundle.js'), 'utf-8') - - assert.equal(svgFiles.length, 1) - assert.match(svgFiles[0], /sprite-[a-f0-9]+\.svg$/) - assert.doesNotMatch(javascript, /data:image\/svg\+xml/) -}) diff --git a/test/mode-contracts.test.mjs b/test/mode-contracts.test.mjs new file mode 100644 index 0000000..048079b --- /dev/null +++ b/test/mode-contracts.test.mjs @@ -0,0 +1,243 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +import { generateNextSprite, generateReactSprite } from '../dist/index.js' + +const GENERATED_FILES = [ + 'icon-data.d.ts', + 'icon-data.js', + 'index.d.ts', + 'index.js', + 'react', + 'sprite.svg', + 'state.json', + 'svg-sprite.manifest.d.ts', + 'svg-sprite.manifest.js', +] + +function createReactFixture(config = {}) { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-')) + const rootDir = path.join(temporaryDir, 'src', 'widgets', 'file-manager', 'svg-sprite') + const iconsDir = path.join(rootDir, 'icons') + const configPath = path.join(rootDir, 'svg-sprite.config.ts') + + fs.mkdirSync(iconsDir, { recursive: true }) + fs.writeFileSync(configPath, `export default ${JSON.stringify({ + name: 'file-manager', + description: 'File manager icons', + ...config, + }, null, 2)}\n`) + fs.writeFileSync( + path.join(iconsDir, 'check.svg'), + '', + ) + fs.writeFileSync( + path.join(iconsDir, 'folder.svg'), + '', + ) + fs.writeFileSync( + path.join(iconsDir, 'folder open.svg'), + '', + ) + + return { rootDir, iconsDir, configPath } +} + +test('generates the current isolated React Vite contract', async () => { + const { rootDir, configPath } = createReactFixture() + const result = await generateReactSprite(configPath, 'vite') + const generatedDir = path.join(rootDir, '.svg-sprite') + + assert.equal(result.name, 'file-manager') + assert.equal(result.iconCount, 3) + assert.equal(result.target, 'vite') + assert.equal(result.manifestPath, path.join(generatedDir, 'svg-sprite.manifest.js')) + assert.deepEqual(fs.readdirSync(generatedDir).sort(), GENERATED_FILES) + + const gitignore = fs.readFileSync(path.join(rootDir, '.gitignore'), 'utf8') + assert.equal(gitignore, '# @generated by @gromlab/svg-sprites. Do not edit.\n/.svg-sprite/\n') + + const sprite = fs.readFileSync(result.spritePath, 'utf8') + assert.match(sprite, /АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ/) + assert.match(sprite, /]+id="check"/) + assert.match(sprite, /]+id="folder"/) + assert.doesNotMatch(sprite, /]+id="(icon-[a-f0-9]{16})"/)?.[1] + assert.ok(unsafeId) + + const component = fs.readFileSync(path.join(generatedDir, 'react', 'react-component.js'), 'utf8') + const componentTypes = fs.readFileSync(path.join(generatedDir, 'react', 'react-component.d.ts'), 'utf8') + const iconData = fs.readFileSync(path.join(generatedDir, 'icon-data.js'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + + assert.match(component, /export const FileManagerIcon/) + assert.match(component, /sprite\.svg\?no-inline/) + assert.match(componentTypes, /FileManagerIconStyle/) + assert.match(componentTypes, /--icon-color-/) + assert.ok(iconData.includes(`"folder open": "${unsafeId}"`)) + assert.match(manifest, /"componentName": "FileManagerIcon"/) + assert.match(manifest, /"mode": "react@vite"/) + assert.match(manifest, /"target": "vite"/) + assert.match(manifest, /"iconCount": 3/) + + const state = JSON.parse(fs.readFileSync(path.join(generatedDir, 'state.json'), 'utf8')) + assert.deepEqual(state.owner, { mode: 'react@vite', contractVersion: 5 }) + assert.equal(state.files.includes('.svg-sprite/react/react-component.js'), true) +}) + +test('supports Webpack target and a custom icons directory', async () => { + const { rootDir, configPath } = createReactFixture({ + name: 'documents', + inputFolder: './assets', + generatedNotice: false, + }) + fs.renameSync(path.join(rootDir, 'icons'), path.join(rootDir, 'assets')) + + const result = await generateReactSprite(configPath, 'webpack') + const component = fs.readFileSync(path.join(result.generatedDir, 'react', 'react-component.js'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + + assert.equal(result.name, 'documents') + assert.equal(result.iconCount, 3) + assert.equal(result.target, 'webpack') + assert.match(component, /@generated by @gromlab\/svg-sprites/) + assert.doesNotMatch(component, /АВТОМАТИЧЕСКИ/) + assert.match(component, /DocumentsIcon/) + assert.match(component, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) + assert.match(manifest, /"target": "webpack"/) +}) + +test('merges inputFolder and inputFiles and deduplicates identical paths', async () => { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-inputs-')) + const rootDir = path.join(temporaryDir, 'svg-sprite') + const iconsDir = path.join(rootDir, 'icons') + const sharedIcon = path.join(temporaryDir, 'shared.svg') + fs.mkdirSync(iconsDir, { recursive: true }) + fs.writeFileSync(path.join(iconsDir, 'local.svg'), '') + fs.writeFileSync(sharedIcon, '') + const configPath = path.join(rootDir, 'config.ts') + fs.writeFileSync(configPath, `export default { name: 'mixed-inputs', inputFiles: [${JSON.stringify(path.relative(rootDir, sharedIcon))}, ${JSON.stringify(path.relative(rootDir, sharedIcon))}] }`) + + const result = await generateReactSprite(configPath, 'vite') + const sprite = fs.readFileSync(result.spritePath, 'utf8') + assert.equal(result.name, 'mixed-inputs') + assert.equal(result.iconCount, 2) + assert.match(sprite, /id="local"/) + assert.match(sprite, /id="shared"/) +}) + +test('supports inputFiles without the default icons directory', async () => { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-files-')) + const rootDir = path.join(temporaryDir, 'svg-sprite') + const sharedIcon = path.join(temporaryDir, 'shared', 'only.svg') + fs.mkdirSync(rootDir, { recursive: true }) + fs.mkdirSync(path.dirname(sharedIcon), { recursive: true }) + fs.writeFileSync(sharedIcon, '') + const configPath = path.join(rootDir, 'config.ts') + fs.writeFileSync(configPath, `export default { inputFiles: [${JSON.stringify(path.relative(rootDir, sharedIcon))}] }`) + + const result = await generateReactSprite(configPath, 'webpack') + assert.equal(result.iconCount, 1) + assert.match(fs.readFileSync(result.spritePath, 'utf8'), /id="only"/) +}) + +test('rejects duplicate names and explicitly missing input folders', async () => { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-input-errors-')) + const rootDir = path.join(temporaryDir, 'svg-sprite') + const first = path.join(temporaryDir, 'one', 'shared.svg') + const second = path.join(temporaryDir, 'two', 'shared.svg') + fs.mkdirSync(rootDir, { recursive: true }) + fs.mkdirSync(path.dirname(first), { recursive: true }) + fs.mkdirSync(path.dirname(second), { recursive: true }) + fs.writeFileSync(first, '') + fs.writeFileSync(second, '') + const duplicateConfig = path.join(rootDir, 'duplicate.ts') + fs.writeFileSync(duplicateConfig, `export default { inputFiles: [${JSON.stringify(path.relative(rootDir, first))}, ${JSON.stringify(path.relative(rootDir, second))}] }`) + await assert.rejects(generateReactSprite(duplicateConfig, 'vite'), /produce the same SVG id "shared"/) + + const missingConfig = path.join(rootDir, 'missing.ts') + fs.writeFileSync(missingConfig, `export default { inputFolder: './missing', inputFiles: [${JSON.stringify(path.relative(rootDir, first))}] }`) + await assert.rejects(generateReactSprite(missingConfig, 'vite'), /Input directory does not exist/) +}) + +test('applies transform options to SVG and React styles', async () => { + const { rootDir, configPath } = createReactFixture({ + transform: { removeSize: false, replaceColors: false, addTransition: false }, + }) + fs.writeFileSync( + path.join(rootDir, 'icons', 'plain.svg'), + '', + ) + + const result = await generateReactSprite(configPath, 'vite') + const sprite = fs.readFileSync(result.spritePath, 'utf8') + const styles = fs.readFileSync(path.join(result.generatedDir, 'react', 'react-component.module.css'), 'utf8') + assert.match(sprite, /width="24"/) + assert.match(sprite, /fill="#(?:123456|123)"/) + assert.doesNotMatch(sprite, /--icon-color-/) + assert.doesNotMatch(styles, /transition-/) +}) + +test('generates every Next.js exact mode without a client boundary', async () => { + const targets = [ + { router: 'app', bundler: 'turbopack' }, + { router: 'app', bundler: 'webpack' }, + { router: 'pages', bundler: 'turbopack' }, + { router: 'pages', bundler: 'webpack' }, + ] + + for (const options of targets) { + const { configPath } = createReactFixture() + const result = await generateNextSprite(configPath, options) + const component = fs.readFileSync(path.join(result.generatedDir, 'react', 'react-component.js'), 'utf8') + const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const mode = `next@${options.router}/${options.bundler}` + assert.equal(result.target, mode) + assert.equal(result.router, options.router) + assert.equal(result.bundler, options.bundler) + assert.match(component, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) + assert.doesNotMatch(component, /['"]use client['"]/) + assert.match(manifest, new RegExp(`"target": ${JSON.stringify(mode)}`)) + } +}) + +test('writer removes only obsolete managed files and preserves user files', async () => { + const { rootDir, configPath } = createReactFixture() + await generateReactSprite(configPath, 'vite') + const statePath = path.join(rootDir, '.svg-sprite', 'state.json') + const state = JSON.parse(fs.readFileSync(statePath, 'utf8')) + state.files.push('.svg-sprite/obsolete.js') + fs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`) + fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'obsolete.js'), '/* @generated by @gromlab/svg-sprites. */\n') + fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'notes.md'), 'Keep me\n') + fs.writeFileSync(path.join(rootDir, 'index.ts'), 'export const userCode = true\n') + + await generateReactSprite(configPath, 'vite') + assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'obsolete.js')), false) + assert.equal(fs.readFileSync(path.join(rootDir, '.svg-sprite', 'notes.md'), 'utf8'), 'Keep me\n') + assert.equal(fs.readFileSync(path.join(rootDir, 'index.ts'), 'utf8'), 'export const userCode = true\n') +}) + +test('writer refuses user content and symbolic links in managed paths', async () => { + const first = createReactFixture() + fs.mkdirSync(path.join(first.rootDir, '.svg-sprite')) + fs.writeFileSync(path.join(first.rootDir, '.svg-sprite', 'index.js'), 'export const userCode = true\n') + await assert.rejects(generateReactSprite(first.configPath, 'vite'), /Refusing to overwrite a user file/) + + const second = createReactFixture() + const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-outside-')) + fs.symlinkSync(outsideDir, path.join(second.rootDir, '.svg-sprite'), 'dir') + await assert.rejects(generateReactSprite(second.configPath, 'vite'), /Symbolic links are not allowed/) + assert.deepEqual(fs.readdirSync(outsideDir), []) +}) + +test('rejects colliding SVG shape IDs', async () => { + const { iconsDir, configPath } = createReactFixture() + const id = `icon-${createHash('sha256').update('folder open').digest('hex').slice(0, 16)}` + fs.writeFileSync(path.join(iconsDir, `${id}.svg`), '') + await assert.rejects(generateReactSprite(configPath, 'vite'), /produce the same SVG id/) +}) diff --git a/test/next-targets.test.mjs b/test/next-targets.test.mjs deleted file mode 100644 index 71c47e9..0000000 --- a/test/next-targets.test.mjs +++ /dev/null @@ -1,204 +0,0 @@ -import assert from 'node:assert/strict' -import { spawn, spawnSync } from 'node:child_process' -import { once } from 'node:events' -import fs from 'node:fs' -import net from 'node:net' -import path from 'node:path' -import test from 'node:test' -import { setTimeout as delay } from 'node:timers/promises' - -import { generateNextSprite } from '../dist/index.js' - -const NEXT_BIN = path.resolve('node_modules/next/dist/bin/next') - -function writeFile(filePath, content) { - fs.mkdirSync(path.dirname(filePath), { recursive: true }) - fs.writeFileSync(filePath, content) -} - -function createNextFixture() { - const rootDir = fs.mkdtempSync(path.join(path.resolve('test'), '.next-fixture-')) - - writeFile( - path.join(rootDir, 'package.json'), - JSON.stringify({ name: 'svg-sprites-next-fixture', private: true }, null, 2), - ) - writeFile( - path.join(rootDir, 'tsconfig.json'), - JSON.stringify({ - compilerOptions: { - jsx: 'preserve', - module: 'esnext', - moduleResolution: 'bundler', - strict: true, - skipLibCheck: true, - }, - }, null, 2), - ) - writeFile( - path.join(rootDir, 'next-env.d.ts'), - '/// \n/// \n', - ) - writeFile( - path.join(rootDir, 'app', 'layout.tsx'), - `export default function Layout({ children }: { children: React.ReactNode }) { - return {children} -} -`, - ) - writeFile( - path.join(rootDir, 'app', 'page.tsx'), - `import { AppIcon } from '../sprites/app' - -export default function Page() { - return
-} -`, - ) - writeFile( - path.join(rootDir, 'pages', 'legacy.tsx'), - `import { PagesIcon } from '../sprites/pages' - -export default function LegacyPage() { - return
-} - -export function getServerSideProps() { - return { props: {} } -} -`, - ) - - for (const name of ['app', 'pages']) { - const spriteRoot = path.join(rootDir, 'sprites', name) - writeFile( - path.join(spriteRoot, 'svg-sprite.config.ts'), - `export default { name: '${name}', generatedNotice: false }\n`, - ) - writeFile( - path.join(spriteRoot, 'icons', 'check.svg'), - '', - ) - } - - return rootDir -} - -function runNext(rootDir, args) { - const result = spawnSync(process.execPath, [NEXT_BIN, ...args], { - cwd: rootDir, - encoding: 'utf-8', - env: { - ...process.env, - NEXT_TELEMETRY_DISABLED: '1', - }, - maxBuffer: 10 * 1024 * 1024, - timeout: 120_000, - }) - - assert.equal( - result.status, - 0, - [result.stdout, result.stderr, result.error?.message].filter(Boolean).join('\n'), - ) -} - -async function getFreePort() { - const server = net.createServer() - server.listen(0, '127.0.0.1') - await once(server, 'listening') - const address = server.address() - assert.ok(address && typeof address === 'object') - const { port } = address - server.close() - await once(server, 'close') - return port -} - -async function startNext(rootDir) { - const port = await getFreePort() - const child = spawn(process.execPath, [NEXT_BIN, 'start', '--hostname', '127.0.0.1', '--port', String(port)], { - cwd: rootDir, - env: { - ...process.env, - NEXT_TELEMETRY_DISABLED: '1', - }, - stdio: ['ignore', 'pipe', 'pipe'], - }) - let output = '' - child.stdout.on('data', (chunk) => { output += chunk }) - child.stderr.on('data', (chunk) => { output += chunk }) - const origin = `http://127.0.0.1:${port}` - - for (let attempt = 0; attempt < 100; attempt++) { - if (child.exitCode !== null) { - throw new Error(`Next.js exited before startup.\n${output}`) - } - - try { - const response = await fetch(origin) - if (response.ok) return { child, origin } - } catch { - // Server is still starting. - } - - await delay(100) - } - - child.kill('SIGKILL') - throw new Error(`Next.js did not start in time.\n${output}`) -} - -async function stopNext(child) { - if (child.exitCode !== null) return - child.kill('SIGTERM') - - await Promise.race([ - once(child, 'exit'), - delay(5_000).then(() => child.kill('SIGKILL')), - ]) -} - -async function assertRenderedSprite(origin, route) { - const response = await fetch(`${origin}${route}`) - assert.equal(response.status, 200) - const html = await response.text() - const href = html.match(/href="([^"]+\.svg)#check"/)?.[1] - - assert.ok(href, `Sprite href not found in ${route}: ${html}`) - assert.doesNotMatch(href, /^(?:data|file|blob):/) - - const spriteResponse = await fetch(new URL(href, origin)) - assert.equal(spriteResponse.status, 200) - assert.match(spriteResponse.headers.get('content-type') ?? '', /image\/svg\+xml/) - assert.match(await spriteResponse.text(), /id="check"/) -} - -test('Next 16.2 renders App and Pages sprites with Turbopack and Webpack', { timeout: 300_000 }, async () => { - const rootDir = createNextFixture() - - try { - for (const bundler of ['turbopack', 'webpack']) { - await generateNextSprite(path.join(rootDir, 'sprites', 'app'), { - router: 'app', - bundler, - }) - await generateNextSprite(path.join(rootDir, 'sprites', 'pages'), { - router: 'pages', - bundler, - }) - - runNext(rootDir, ['build', `--${bundler}`]) - const { child, origin } = await startNext(rootDir) - - try { - await assertRenderedSprite(origin, '/') - await assertRenderedSprite(origin, '/legacy') - } finally { - await stopNext(child) - } - } - } finally { - fs.rmSync(rootDir, { recursive: true, force: true }) - } -}) diff --git a/test/react-mode.test.mjs b/test/react-mode.test.mjs deleted file mode 100644 index e83a3d9..0000000 --- a/test/react-mode.test.mjs +++ /dev/null @@ -1,590 +0,0 @@ -import assert from 'node:assert/strict' -import { spawnSync } from 'node:child_process' -import { createHash } from 'node:crypto' -import fs from 'node:fs' -import os from 'node:os' -import path from 'node:path' -import test from 'node:test' -import ts from 'typescript' - -import { generateNextSprite, generateReactSprite } from '../dist/index.js' - -const GENERATED_FILES = [ - '.svg-sprites.manifest.json', - 'react-component.tsx', - 'sprite.svg', - 'styles.module.css', - 'types.ts', -] - -function createReactFixture() { - const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-')) - const rootDir = path.join(temporaryDir, 'src', 'widgets', 'file-manager', 'svg-sprite') - const iconsDir = path.join(rootDir, 'icons') - - fs.mkdirSync(iconsDir, { recursive: true }) - fs.writeFileSync( - path.join(rootDir, 'svg-sprite.config.ts'), - `export default { description: 'File manager icons' }\n`, - ) - fs.writeFileSync( - path.join(iconsDir, 'check.svg'), - '', - ) - fs.writeFileSync( - path.join(iconsDir, 'folder.svg'), - '', - ) - fs.writeFileSync( - path.join(iconsDir, 'folder open.svg'), - '', - ) - - return { rootDir, iconsDir } -} - -test('generates an isolated React sprite module', async () => { - const { rootDir } = createReactFixture() - const result = await generateReactSprite(rootDir, 'vite') - const generatedDir = path.join(rootDir, 'generated') - - assert.equal(result.name, 'file-manager') - assert.equal(result.iconCount, 3) - assert.equal(result.target, 'vite') - assert.equal(result.manifestPath, path.join(rootDir, 'manifest.ts')) - assert.deepEqual(fs.readdirSync(generatedDir).sort(), GENERATED_FILES) - - const gitignore = fs.readFileSync(path.join(rootDir, '.gitignore'), 'utf-8') - assert.match(gitignore, /@generated by @gromlab\/svg-sprites/) - assert.match(gitignore, /^\/generated\/$/m) - assert.match(gitignore, /^\/index\.ts$/m) - assert.match(gitignore, /^\/manifest\.ts$/m) - - const sprite = fs.readFileSync(path.join(generatedDir, 'sprite.svg'), 'utf-8') - assert.match(sprite, /АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ/) - assert.match(sprite, /Генератор: @gromlab\/svg-sprites/) - assert.match(sprite, /]+id="check"/) - assert.match(sprite, /]+id="folder"/) - assert.match(sprite, /]*>\n @@ -15,6 +16,12 @@ + + diff --git a/integration/package-lock.json b/integration/package-lock.json index ec322f1..8514732 100644 --- a/integration/package-lock.json +++ b/integration/package-lock.json @@ -34,11 +34,12 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", + "lit": "^3.3.3", "react": "^19.2.5", - "react-colorful": "^5.6.1", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vanilla-colorful": "^0.7.2" }, "engines": { "node": ">=18" diff --git a/integration/tests/apps.spec.ts b/integration/tests/apps.spec.ts index 79c67dc..3a4de4e 100644 --- a/integration/tests/apps.spec.ts +++ b/integration/tests/apps.spec.ts @@ -104,6 +104,8 @@ for (const app of apps) { try { await waitForServer(server, origin) await page.goto(origin) + await page.waitForLoadState('networkidle') + expect(browserErrors).toEqual([]) const icon = page.getByTestId('icon') await expect(icon).toBeVisible() @@ -122,6 +124,47 @@ for (const app of apps) { expect(spriteResponse.headers.get('content-type') ?? '').toMatch(/image\/svg\+xml/) expect(await spriteResponse.text()).toMatch(/id="check"/) + const viewer = page.locator('gromlab-sprite-viewer') + await expect(viewer).toBeVisible() + await expect(viewer.locator('[data-sprite-viewer]')).toBeVisible() + + const viewerCard = viewer.locator('[data-icon-name="check"]') + await expect(viewerCard).toBeVisible() + const viewerHref = await viewerCard.locator('use').getAttribute('href') + expect(viewerHref).toBeTruthy() + expect(viewerHref!.split('#')[1]).toBe('check') + expect(new URL(viewerHref!.split('#')[0], page.url()).href).toBe(spriteUrl.href) + + await viewerCard.click() + const dialog = viewer.getByRole('dialog', { name: 'check' }) + await expect(dialog).toBeVisible() + await expect(dialog.getByRole('tab', { name: 'SVG' })).toBeVisible() + if (app.id.startsWith('standalone')) { + await expect(dialog.getByRole('tab', { name: 'React' })).toHaveCount(0) + } else { + await expect(dialog.getByRole('tab', { name: 'React' })).toBeVisible() + } + + const colorSwatch = dialog.getByRole('button', { name: /Изменить цвет --icon-color-1/ }) + await colorSwatch.click() + const colorPicker = dialog.locator('gromlab-hex-color-picker') + await expect(colorPicker).toBeVisible() + const saturationBounds = await colorPicker.locator('[part="saturation"]').boundingBox() + const hueBounds = await colorPicker.locator('[part="hue"]').boundingBox() + expect(saturationBounds?.height).toBeGreaterThan(100) + expect(hueBounds?.height).toBeGreaterThanOrEqual(20) + const popoverBounds = await dialog.locator('.gromlab-sprite-viewer__color-popover').boundingBox() + const colorInputBounds = await dialog.locator('gromlab-hex-input input').boundingBox() + expect(colorInputBounds!.x).toBeGreaterThanOrEqual(popoverBounds!.x) + expect(colorInputBounds!.x + colorInputBounds!.width).toBeLessThanOrEqual( + popoverBounds!.x + popoverBounds!.width, + ) + await dialog.locator('.gromlab-sprite-viewer__color-label').click() + await expect(colorPicker).toHaveCount(0) + + await dialog.getByRole('button', { name: 'Закрыть' }).click() + await expect(dialog).toHaveCount(0) + const screenshot = PNG.sync.read(await icon.screenshot()) let coloredPixels = 0 for (let index = 0; index < screenshot.data.length; index += 4) { diff --git a/package-lock.json b/package-lock.json index 5e63fdd..d6411fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,11 +22,12 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", + "lit": "^3.3.3", "react": "^19.2.5", - "react-colorful": "^5.6.1", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vanilla-colorful": "^0.7.2" }, "engines": { "node": ">=18" @@ -670,6 +671,23 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, "node_modules/@resvg/resvg-js": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", @@ -1307,6 +1325,13 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vinyl": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", @@ -2171,6 +2196,40 @@ "dev": true, "license": "MIT" }, + "node_modules/lit": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", + "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -2489,17 +2548,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "node_modules/react-dom": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", @@ -3053,6 +3101,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/vanilla-colorful": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/vanilla-colorful/-/vanilla-colorful-0.7.2.tgz", + "integrity": "sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==", + "dev": true, + "license": "MIT" + }, "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", diff --git a/package.json b/package.json index 6c68403..1056b34 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@gromlab/svg-sprites", "version": "1.1.5", - "description": "Generate SVG sprites and typed icon components for React and Next.js", + "description": "Generate SVG sprites and typed icon components for standalone, React, and Next.js", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -13,18 +13,35 @@ "./react": { "types": "./dist/react.d.ts", "import": "./dist/react.js" + }, + "./viewer": { + "types": "./dist/viewer.d.ts", + "import": "./dist/viewer.js" + }, + "./viewer/element": { + "types": "./dist/viewer-element.d.ts", + "import": "./dist/viewer-element.js" } }, "bin": { "svg-sprites": "./dist/cli.js" }, "files": [ + "dist/*.js", + "dist/*.js.map", + "dist/*.d.ts", "dist/index.js", "dist/index.js.map", "dist/index.d.ts", "dist/react.js", "dist/react.js.map", "dist/react.d.ts", + "dist/viewer.js", + "dist/viewer.js.map", + "dist/viewer.d.ts", + "dist/viewer-element.js", + "dist/viewer-element.js.map", + "dist/viewer-element.d.ts", "dist/cli.js", "dist/cli.js.map", "dist/cli.d.ts", @@ -39,11 +56,11 @@ ], "scripts": { "build": "npm run build:package", - "build:package": "tsup", + "build:package": "tsup && tsup --config tsup.viewer.config.ts", "build:skill": "node skills/svg-sprites/build.mjs", "check:skill": "node skills/svg-sprites/build.mjs --check", "dev": "tsup --watch", - "test": "tsup && node --test test/*.test.mjs", + "test": "npm run build:package && node --test test/*.test.mjs", "typecheck": "tsc --noEmit", "integration:install": "npm ci --prefix integration", "integration:generate": "npm run build:package && npm run generate --prefix integration", @@ -100,10 +117,11 @@ "@types/react-dom": "^19.2.3", "@types/svg-sprite": "^0.0.39", "jsdom": "^26.1.0", + "lit": "^3.3.3", "react": "^19.2.5", - "react-colorful": "^5.6.1", "react-dom": "^19.2.5", "tsup": "^8.4.0", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vanilla-colorful": "^0.7.2" } } diff --git a/src/react/sprite-viewer-dialog.tsx b/src/react/sprite-viewer-dialog.tsx deleted file mode 100644 index 62c6a82..0000000 --- a/src/react/sprite-viewer-dialog.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import { HexColorInput, HexColorPicker } from 'react-colorful' -import { useEffect, useId, useRef, useState } from 'react' -import type { CSSProperties } from 'react' -import { - generateViewerCode, - highlightViewerCode, - normalizeHexColor, - tabsForFormat, - viewBoxSize, -} from './sprite-viewer-code.js' -import type { SpriteViewerTab } from './sprite-viewer-code.js' -import type { SpriteManifest, SpriteManifestIcon } from './types.js' - -type SpriteViewerDialogProps = { - manifest: SpriteManifest - icon: SpriteManifestIcon - colorTheme: 'light' | 'dark' - onClose: () => void -} - -type ColorControlProps = { - label: string - value: string - onChange: (value: string) => void -} - -function ColorControl({ label, value, onChange }: ColorControlProps) { - const [open, setOpen] = useState(false) - const swatchRef = useRef(null) - const popoverRef = useRef(null) - const popoverId = useId() - - useEffect(() => { - if (!open) return - - function handlePointerDown(event: PointerEvent) { - const target = event.target as Node - const insideSwatch = swatchRef.current?.contains(target) - const insidePopover = popoverRef.current?.contains(target) - if (!insideSwatch && !insidePopover) setOpen(false) - } - - document.addEventListener('pointerdown', handlePointerDown, true) - return () => document.removeEventListener('pointerdown', handlePointerDown, true) - }, [open]) - - return ( -
-
- ) -} - -function initialColors(icon: SpriteManifestIcon, currentColor: string) { - return Object.fromEntries(icon.colors.map(({ variable, fallback }) => [ - variable, - normalizeHexColor(fallback, currentColor), - ])) -} - -export function SpriteViewerDialog({ manifest, icon, colorTheme, onClose }: SpriteViewerDialogProps) { - const dialogRef = useRef(null) - const titleId = useId() - const tabsId = useId() - const tabs = tabsForFormat(manifest.format) - const [activeTab, setActiveTab] = useState('react') - const themeColor = colorTheme === 'dark' ? '#e5e5e5' : '#1a1a1a' - const [colors, setColors] = useState>(() => initialColors(icon, themeColor)) - const [colorOverrides, setColorOverrides] = useState>({}) - const [cssColor, setCssColor] = useState(themeColor) - const [cssColorOverridden, setCssColorOverridden] = useState(false) - const [copied, setCopied] = useState(false) - - useEffect(() => { - const dialog = dialogRef.current - if (!dialog) return - - dialog.showModal() - - return () => { - if (dialog.open) dialog.close() - } - }, [icon]) - - useEffect(() => { - setColors({ ...initialColors(icon, themeColor), ...colorOverrides }) - if (!cssColorOverridden) setCssColor(themeColor) - }, [colorTheme, icon]) - - useEffect(() => { - if (!copied) return - const timeout = setTimeout(() => setCopied(false), 1500) - return () => clearTimeout(timeout) - }, [copied]) - - const previewStyle = Object.fromEntries( - Object.entries(colors).map(([variable, color]) => [variable, color]), - ) as CSSProperties - const href = `${manifest.spriteUrl}#${icon.id}` - const dimensions = viewBoxSize(icon.viewBox) - const code = generateViewerCode({ manifest, icon, tab: activeTab, colorOverrides, cssColor }) - - function handleColorChange(variable: string, color: string) { - const normalized = normalizeHexColor(color) - setColors((current) => ({ ...current, [variable]: normalized })) - setColorOverrides((current) => ({ ...current, [variable]: normalized })) - } - - function handleCssColorChange(color: string) { - setCssColor(normalizeHexColor(color)) - setCssColorOverridden(true) - } - - function handleBackdropClick(event: React.MouseEvent) { - if (event.target !== event.currentTarget) return - const bounds = event.currentTarget.getBoundingClientRect() - const outside = event.clientX < bounds.left - || event.clientX > bounds.right - || event.clientY < bounds.top - || event.clientY > bounds.bottom - if (outside) onClose() - } - - function handleTabKeyDown(event: React.KeyboardEvent, tabIndex: number) { - let nextIndex: number | null = null - if (event.key === 'ArrowRight') nextIndex = (tabIndex + 1) % tabs.length - if (event.key === 'ArrowLeft') nextIndex = (tabIndex - 1 + tabs.length) % tabs.length - if (event.key === 'Home') nextIndex = 0 - if (event.key === 'End') nextIndex = tabs.length - 1 - if (nextIndex === null) return - - event.preventDefault() - setActiveTab(tabs[nextIndex].id) - const buttons = event.currentTarget.parentElement?.querySelectorAll('[role="tab"]') - buttons?.[nextIndex]?.focus() - } - - async function copyCode() { - if (!globalThis.navigator?.clipboard) return - try { - await globalThis.navigator.clipboard.writeText(code.code) - setCopied(true) - } catch { - setCopied(false) - } - } - - function renderPreview() { - if (activeTab === 'img') { - return {icon.name} - } - if (activeTab === 'css') { - return ( -
- ) - } - return ( - - - - ) - } - - return ( - { - event.preventDefault() - onClose() - }} - onClick={handleBackdropClick} - > -
- - -
-
- {renderPreview()} -
-
- -
-

{icon.name}

- {dimensions && {dimensions}} -
-

- {manifest.name} · {manifest.format} · {manifest.target} -

- -
- {(activeTab === 'react' || activeTab === 'svg') && icon.colors.length > 0 && ( - <> -

- Цвета применяются к превью через CSS-переменные и попадут в пример кода. -

-

CSS Variables

- {icon.colors.map(({ variable, fallback }) => ( - handleColorChange(variable, color)} - /> - ))} - - )} - {(activeTab === 'react' || activeTab === 'svg') && icon.colors.length === 0 && ( -

У иконки нет настраиваемых цветовых переменных.

- )} - {activeTab === 'img' && ( -

- IMG изолирует SVG: CSS-переменные и currentColor внутрь изображения не передаются. -

- )} - {activeTab === 'css' && ( - <> -

- CSS mask отображает иконку одним цветом через background-color. -

- - - )} -
- -
- {tabs.map((tab, tabIndex) => ( - - ))} -
- -
-
- -
-
-
- ) -} diff --git a/src/react/sprite-viewer.tsx b/src/react/sprite-viewer.tsx index 7992a69..792da76 100644 --- a/src/react/sprite-viewer.tsx +++ b/src/react/sprite-viewer.tsx @@ -1,19 +1,10 @@ 'use client' -import { useDeferredValue, useEffect, useState } from 'react' +import { createElement, useEffect, useRef } from 'react' import type { CSSProperties, ReactElement } from 'react' -import { SpriteViewerDialog } from './sprite-viewer-dialog.js' -import { SPRITE_VIEWER_STYLES } from './sprite-viewer-styles.js' -import type { - SpriteManifest, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerSource, - SpriteViewerSources, -} from './types.js' +import type { SpriteViewerColorTheme, SpriteViewerSources } from '../viewer/types.js' -export type SpriteViewerColorTheme = 'auto' | 'light' | 'dark' +export type { SpriteViewerColorTheme } from '../viewer/types.js' export type SpriteViewerProps = { sources: SpriteViewerSources @@ -26,96 +17,17 @@ export type SpriteViewerProps = { style?: CSSProperties } -type SelectedIcon = { - key: string - manifest: SpriteManifest - icon: SpriteManifestIcon +type ViewerElement = HTMLElement & { + sources: SpriteViewerSources + viewerTitle: string + colorTheme: SpriteViewerColorTheme + themeControlled: boolean + showThemeToggle: boolean } -const manifestLoaderCache = new WeakMap>() +type ThemeChangeEvent = CustomEvent<{ theme: SpriteViewerColorTheme }> -function compareManifests(left: SpriteManifest, right: SpriteManifest): number { - return left.name < right.name ? -1 : left.name > right.name ? 1 : 0 -} - -function sourceArray(sources: SpriteViewerSources): readonly SpriteViewerSource[] { - return Array.isArray(sources) - ? sources - : Object.values(sources as Readonly>) -} - -function isSpriteManifest(value: unknown): value is SpriteManifest { - if (!value || typeof value !== 'object') return false - const manifest = value as Partial - return manifest.schemaVersion === 1 - && manifest.generator === '@gromlab/svg-sprites' - && typeof manifest.name === 'string' - && typeof manifest.componentName === 'string' - && typeof manifest.spriteUrl === 'string' - && Array.isArray(manifest.icons) -} - -function manifestFromModule(value: SpriteManifest | SpriteManifestModule): SpriteManifest { - if (isSpriteManifest(value)) return value - if (isSpriteManifest(value.default)) return value.default - if (isSpriteManifest(value.spriteManifest)) return value.spriteManifest - throw new Error('The loaded module does not export a valid SVG sprite manifest.') -} - -async function resolveSource(source: SpriteViewerSource): Promise { - if (typeof source !== 'function') return source - - const cached = manifestLoaderCache.get(source) - if (cached) return cached - - const pending = Promise.resolve().then(source).then(manifestFromModule) - manifestLoaderCache.set(source, pending) - void pending.catch(() => { - if (manifestLoaderCache.get(source) === pending) manifestLoaderCache.delete(source) - }) - return pending -} - -function directManifests(sources: SpriteViewerSources): SpriteManifest[] { - return sourceArray(sources) - .filter((source): source is SpriteManifest => typeof source !== 'function') - .sort(compareManifests) -} - -function countLabel(value: number, forms: readonly [string, string, string]): string { - const modulo100 = value % 100 - const modulo10 = value % 10 - const form = modulo100 >= 11 && modulo100 <= 19 - ? forms[2] - : modulo10 === 1 - ? forms[0] - : modulo10 >= 2 && modulo10 <= 4 - ? forms[1] - : forms[2] - return `${value} ${form}` -} - -function currentSystemTheme(): 'light' | 'dark' { - return globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' -} - -function useSystemTheme(): 'light' | 'dark' { - const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(currentSystemTheme) - - useEffect(() => { - const media = globalThis.matchMedia?.('(prefers-color-scheme: dark)') - if (!media) return - - const update = () => setSystemTheme(media.matches ? 'dark' : 'light') - update() - media.addEventListener('change', update) - return () => media.removeEventListener('change', update) - }, []) - - return systemTheme -} - -/** Интерактивный каталог локальных React- и Next.js-спрайтов. */ +/** React bridge к единому framework-neutral Viewer Web Component. */ export function SpriteViewer({ sources, title = 'SVG Sprites', @@ -124,178 +36,42 @@ export function SpriteViewer({ className, style, }: SpriteViewerProps): ReactElement { - const [manifests, setManifests] = useState(() => directManifests(sources)) - const [errors, setErrors] = useState([]) - const [loading, setLoading] = useState(() => sourceArray(sources).some((source) => typeof source === 'function')) - const [query, setQuery] = useState('') - const [localColorTheme, setLocalColorTheme] = useState('auto') - const [selected, setSelected] = useState(null) - const systemTheme = useSystemTheme() - const activeColorTheme = colorTheme ?? localColorTheme - const resolvedColorTheme = activeColorTheme === 'auto' ? systemTheme : activeColorTheme - const canToggleTheme = colorTheme === undefined || onColorThemeChange !== undefined - const deferredQuery = useDeferredValue(query.trim().toLowerCase()) + const elementRef = useRef(null) useEffect(() => { let active = true - const allSources = sourceArray(sources) - const direct = directManifests(sources) - const loaders = allSources.filter((source) => typeof source === 'function') + let element: ViewerElement | null = null - setManifests(direct) - setErrors([]) - setLoading(loaders.length > 0) - setSelected(null) + const handleThemeChange = (event: Event) => { + onColorThemeChange?.((event as ThemeChangeEvent).detail.theme) + } - if (loaders.length === 0) return () => { active = false } + void import('../viewer-element.js').then(async ({ defineSpriteViewerElement }) => { + defineSpriteViewerElement() + await customElements.whenDefined('gromlab-sprite-viewer') + if (!active || !elementRef.current) return - Promise.allSettled(loaders.map(resolveSource)).then((results) => { - if (!active) return - - const loaded: SpriteManifest[] = [] - const failures: string[] = [] - - for (const result of results) { - if (result.status === 'fulfilled') loaded.push(result.value) - else failures.push(result.reason instanceof Error ? result.reason.message : String(result.reason)) - } - - setManifests([...direct, ...loaded].sort(compareManifests)) - setErrors(failures) - setLoading(false) + element = elementRef.current + const wasThemeControlled = element.themeControlled + element.sources = sources + element.viewerTitle = title + if (colorTheme !== undefined) element.colorTheme = colorTheme + else if (wasThemeControlled) element.colorTheme = 'auto' + element.themeControlled = colorTheme !== undefined + element.showThemeToggle = colorTheme === undefined || onColorThemeChange !== undefined + element.addEventListener('color-theme-change', handleThemeChange) }) - return () => { active = false } - }, [sources]) + return () => { + active = false + element?.removeEventListener('color-theme-change', handleThemeChange) + } + }, [colorTheme, onColorThemeChange, sources, title]) - const visibleGroups = manifests - .map((manifest) => ({ - manifest, - icons: manifest.icons.filter((icon) => ( - deferredQuery === '' - || icon.name.toLowerCase().includes(deferredQuery) - || manifest.name.toLowerCase().includes(deferredQuery) - )), - })) - .filter((group) => group.icons.length > 0) - - const totalIcons = manifests.reduce((total, manifest) => total + manifest.iconCount, 0) - const visibleIcons = visibleGroups.reduce((total, group) => total + group.icons.length, 0) - const rootClassName = ['gromlab-sprite-viewer', className].filter(Boolean).join(' ') - - function toggleTheme() { - const nextTheme = resolvedColorTheme === 'dark' ? 'light' : 'dark' - if (colorTheme === undefined) setLocalColorTheme(nextTheme) - onColorThemeChange?.(nextTheme) - } - - return ( -
- -
-

{title}

- - {countLabel(manifests.length, ['спрайт', 'спрайта', 'спрайтов'])} - {' · '} - {countLabel(totalIcons, ['иконка', 'иконки', 'иконок'])} - {deferredQuery && ` · найдено ${visibleIcons}`} - -
- setQuery(event.currentTarget.value)} - placeholder="Найти иконку" - aria-label="Поиск иконок" - /> - {canToggleTheme && ( - - )} -
-
- - {errors.length > 0 && ( -
- {errors.map((error, index) =>
{error}
)} -
- )} - - {visibleGroups.map(({ manifest, icons }) => ( -
-
-

{manifest.name}

- {manifest.format} - {icons.length} - {manifest.description && ( -

{manifest.description}

- )} -
-
- {icons.map((icon) => { - const key = `${manifest.name}:${manifest.spriteUrl}:${icon.id}` - return ( - - ) - })} -
-
- ))} - - {visibleGroups.length === 0 && (!loading || manifests.length > 0) && ( -
- {manifests.length === 0 ? 'Спрайты не подключены' : 'Иконки не найдены'} -
- )} - {loading && manifests.length === 0 && ( -
Загрузка спрайтов...
- )} - - - - {selected && ( - setSelected(null)} - /> - )} -
- ) + return createElement('gromlab-sprite-viewer', { + ref: elementRef, + className, + style, + 'data-sprite-viewer-host': '', + }) } diff --git a/src/react/types.ts b/src/react/types.ts index d709dc5..8bf1fc7 100644 --- a/src/react/types.ts +++ b/src/react/types.ts @@ -1,41 +1,24 @@ import type { NextAssetTarget, ReactAssetTarget, ReactSpriteMode } from '../targets/types.js' +import type { + SpriteViewerManifest, + SpriteViewerManifestColor, + SpriteViewerManifestIcon, + SpriteViewerManifestLoader, + SpriteViewerManifestModule, + SpriteViewerSource, + SpriteViewerSources, +} from '../viewer/types.js' -export type SpriteManifestColor = { - variable: `--icon-color-${number}` - fallback: string -} +export type SpriteManifestColor = SpriteViewerManifestColor +export type SpriteManifestIcon = SpriteViewerManifestIcon -export type SpriteManifestIcon = { - name: string - id: string - viewBox: string | null - colors: readonly SpriteManifestColor[] -} - -/** Публичные данные одного сгенерированного React-спрайта. */ -export type SpriteManifest = { - schemaVersion: 1 - generator: '@gromlab/svg-sprites' - name: string - description?: string +/** Публичные данные одного сгенерированного React- или Next.js-спрайта. */ +export type SpriteManifest = SpriteViewerManifest & { componentName: string mode?: ReactSpriteMode | NextAssetTarget target: ReactAssetTarget | NextAssetTarget - format: 'stack' | 'symbol' - iconCount: number - spriteUrl: string - icons: readonly SpriteManifestIcon[] } -export type SpriteManifestModule = { - default?: SpriteManifest - spriteManifest?: SpriteManifest -} - -export type SpriteManifestLoader = () => Promise -export type SpriteViewerSource = SpriteManifest | SpriteManifestLoader - -/** Массив источников либо результат import.meta.glob. */ -export type SpriteViewerSources = - | readonly SpriteViewerSource[] - | Readonly> +export type SpriteManifestModule = SpriteViewerManifestModule +export type SpriteManifestLoader = SpriteViewerManifestLoader +export type { SpriteViewerSource, SpriteViewerSources } diff --git a/src/viewer-element.ts b/src/viewer-element.ts new file mode 100644 index 0000000..d7466f3 --- /dev/null +++ b/src/viewer-element.ts @@ -0,0 +1,18 @@ +import { defineSpriteViewerElement } from './viewer/element.js' + +defineSpriteViewerElement() + +export { defineSpriteViewerElement } from './viewer/element.js' +export type { + SpriteViewerColorTheme, + SpriteViewerElement, + SpriteViewerManifest, + SpriteViewerManifestColor, + SpriteViewerManifestIcon, + SpriteViewerManifestLoader, + SpriteViewerManifestModule, + SpriteViewerManifestUsage, + SpriteViewerRemoteSource, + SpriteViewerSource, + SpriteViewerSources, +} from './viewer/types.js' diff --git a/src/viewer.ts b/src/viewer.ts new file mode 100644 index 0000000..2ad3b1e --- /dev/null +++ b/src/viewer.ts @@ -0,0 +1,14 @@ +export { defineSpriteViewerElement } from './viewer/element.js' +export type { + SpriteViewerColorTheme, + SpriteViewerElement, + SpriteViewerManifest, + SpriteViewerManifestColor, + SpriteViewerManifestIcon, + SpriteViewerManifestLoader, + SpriteViewerManifestModule, + SpriteViewerManifestUsage, + SpriteViewerRemoteSource, + SpriteViewerSource, + SpriteViewerSources, +} from './viewer/types.js' diff --git a/src/react/sprite-viewer-code.ts b/src/viewer/code.ts similarity index 76% rename from src/react/sprite-viewer-code.ts rename to src/viewer/code.ts index 0d7d25f..890794b 100644 --- a/src/react/sprite-viewer-code.ts +++ b/src/viewer/code.ts @@ -1,19 +1,29 @@ -import type { SpriteManifest, SpriteManifestIcon } from './types.js' +import type { SpriteViewerManifest, SpriteViewerManifestIcon } from './types.js' -export type SpriteViewerTab = 'react' | 'svg' | 'img' | 'css' +export type SpriteViewerTab = 'react' | 'vue' | 'svg' | 'img' | 'css' export type SpriteViewerCodeLanguage = 'tsx' | 'html' | 'css' -export const SPRITE_VIEWER_TABS: ReadonlyArray<{ id: SpriteViewerTab; label: string }> = [ - { id: 'react', label: 'React' }, +const COMMON_TABS: ReadonlyArray<{ id: SpriteViewerTab; label: string }> = [ { id: 'svg', label: 'SVG' }, { id: 'img', label: 'IMG' }, { id: 'css', label: 'CSS' }, ] -export function tabsForFormat(format: SpriteManifest['format']) { - return format === 'stack' - ? SPRITE_VIEWER_TABS - : SPRITE_VIEWER_TABS.filter((tab) => tab.id === 'react' || tab.id === 'svg') +function usageForManifest(manifest: SpriteViewerManifest): { framework: 'react' | 'vue'; componentName: string } | null { + if (manifest.usage) return manifest.usage + if (manifest.componentName) return { framework: 'react', componentName: manifest.componentName } + return null +} + +export function tabsForManifest(manifest: SpriteViewerManifest): ReadonlyArray<{ id: SpriteViewerTab; label: string }> { + const usage = usageForManifest(manifest) + const frameworkTab = usage + ? [{ id: usage.framework, label: usage.framework === 'react' ? 'React' : 'Vue' } as const] + : [] + const tabs = [...frameworkTab, ...COMMON_TABS] + return manifest.format === 'stack' + ? tabs + : tabs.filter((tab) => tab.id === 'react' || tab.id === 'vue' || tab.id === 'svg') } export function viewBoxSize(viewBox: string | null): string | null { @@ -76,8 +86,8 @@ function cssUrl(value: string): string { } export function generateViewerCode(options: { - manifest: SpriteManifest - icon: SpriteManifestIcon + manifest: SpriteViewerManifest + icon: SpriteViewerManifestIcon tab: SpriteViewerTab colorOverrides: Readonly> cssColor: string @@ -86,16 +96,29 @@ export function generateViewerCode(options: { const href = `${manifest.spriteUrl}#${icon.id}` const overrides = styleLines(colorOverrides) - if (tab === 'react') { + if (tab === 'react' || tab === 'vue') { + const usage = usageForManifest(manifest) + if (!usage) throw new Error(`The ${tab} code tab requires component metadata.`) + + if (tab === 'vue') { + const style = Object.entries(colorOverrides) + .map(([variable, color]) => `${JSON.stringify(variable)}: ${JSON.stringify(color)}`) + .join(', ') + return { + code: `<${usage.componentName} icon=${JSON.stringify(icon.name)}${style ? ` :style="{ ${htmlAttribute(style)} }"` : ''} />`, + language: 'html', + } + } + if (overrides.length === 0) { return { - code: `<${manifest.componentName} icon=${JSON.stringify(icon.name)} />`, + code: `<${usage.componentName} icon=${JSON.stringify(icon.name)} />`, language: 'tsx', } } return { code: [ - `<${manifest.componentName}`, + `<${usage.componentName}`, ` icon=${JSON.stringify(icon.name)}`, ' style={{', ...overrides, diff --git a/src/viewer/element.ts b/src/viewer/element.ts new file mode 100644 index 0000000..e054f38 --- /dev/null +++ b/src/viewer/element.ts @@ -0,0 +1,650 @@ +import { LitElement, html, nothing, unsafeCSS } from 'lit' +import type { PropertyValues, TemplateResult } from 'lit' +import { ifDefined } from 'lit/directives/if-defined.js' +import { styleMap } from 'lit/directives/style-map.js' +import { unsafeHTML } from 'lit/directives/unsafe-html.js' +import { + generateViewerCode, + highlightViewerCode, + normalizeHexColor, + tabsForManifest, + viewBoxSize, +} from './code.js' +import type { SpriteViewerTab } from './code.js' +import { + compareManifests, + isRemoteSource, + normalizeSpriteViewerManifest, + resolveViewerSource, + sourceArray, +} from './source.js' +import { SPRITE_VIEWER_STYLES } from './styles.js' +import type { + SpriteViewerColorTheme, + SpriteViewerManifest, + SpriteViewerManifestIcon, + SpriteViewerSource, + SpriteViewerSources, +} from './types.js' + +const ELEMENT_NAME = 'gromlab-sprite-viewer' +const COLOR_PICKER_NAME = 'gromlab-hex-color-picker' +const COLOR_INPUT_NAME = 'gromlab-hex-input' + +let colorElementsPromise: Promise | undefined + +function defineColorElements(): Promise { + if (customElements.get(COLOR_PICKER_NAME) && customElements.get(COLOR_INPUT_NAME)) return Promise.resolve() + if (colorElementsPromise) return colorElementsPromise + + colorElementsPromise = Promise.all([ + import('vanilla-colorful/lib/entrypoints/hex'), + import('vanilla-colorful/lib/entrypoints/hex-input'), + ]).then(([{ HexBase }, { HexInputBase }]) => { + if (!customElements.get(COLOR_PICKER_NAME)) { + customElements.define(COLOR_PICKER_NAME, class extends HexBase {}) + } + if (!customElements.get(COLOR_INPUT_NAME)) { + customElements.define(COLOR_INPUT_NAME, class extends HexInputBase {}) + } + }) + + return colorElementsPromise +} + +function countLabel(value: number, forms: readonly [string, string, string]): string { + const modulo100 = value % 100 + const modulo10 = value % 10 + const form = modulo100 >= 11 && modulo100 <= 19 + ? forms[2] + : modulo10 === 1 + ? forms[0] + : modulo10 >= 2 && modulo10 <= 4 + ? forms[1] + : forms[2] + return `${value} ${form}` +} + +function currentSystemTheme(): 'light' | 'dark' { + return globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +} + +function initialColors(icon: SpriteViewerManifestIcon, currentColor: string): Record { + return Object.fromEntries(icon.colors.map(({ variable, fallback }) => [ + variable, + normalizeHexColor(fallback, currentColor), + ])) +} + +type SelectedIcon = { + key: string + manifest: SpriteViewerManifest + icon: SpriteViewerManifestIcon +} + +type ColorChangedEvent = CustomEvent<{ value: string }> + +class GromlabSpriteViewerElement extends LitElement { + static properties = { + sources: { attribute: false }, + viewerTitle: { attribute: 'viewer-title' }, + colorTheme: { attribute: 'color-theme' }, + themeControlled: { attribute: false }, + showThemeToggle: { attribute: false }, + manifestUrl: { attribute: 'manifest-url' }, + spriteUrl: { attribute: 'sprite-url' }, + _manifests: { state: true }, + _errors: { state: true }, + _loading: { state: true }, + _query: { state: true }, + _systemTheme: { state: true }, + _selected: { state: true }, + _activeTab: { state: true }, + _colors: { state: true }, + _colorOverrides: { state: true }, + _cssColor: { state: true }, + _cssColorOverridden: { state: true }, + _openColorControl: { state: true }, + _copied: { state: true }, + } + + static styles = unsafeCSS(SPRITE_VIEWER_STYLES) + + declare sources: SpriteViewerSources + declare viewerTitle: string + declare colorTheme: SpriteViewerColorTheme + declare themeControlled: boolean + declare showThemeToggle: boolean + declare manifestUrl?: string + declare spriteUrl?: string + + declare private _manifests: SpriteViewerManifest[] + declare private _errors: string[] + declare private _loading: boolean + declare private _query: string + declare private _systemTheme: 'light' | 'dark' + declare private _selected: SelectedIcon | null + declare private _activeTab: SpriteViewerTab + declare private _colors: Record + declare private _colorOverrides: Record + declare private _cssColor: string + declare private _cssColorOverridden: boolean + declare private _openColorControl: string | null + declare private _copied: boolean + private _loadVersion = 0 + private _copyTimeout?: ReturnType + private _media?: MediaQueryList + + constructor() { + super() + this.sources = [] + this.viewerTitle = 'SVG Sprites' + this.colorTheme = 'auto' + this.themeControlled = false + this.showThemeToggle = true + this._manifests = [] + this._errors = [] + this._loading = false + this._query = '' + this._systemTheme = currentSystemTheme() + this._selected = null + this._activeTab = 'svg' + this._colors = {} + this._colorOverrides = {} + this._cssColor = '#1a1a1a' + this._cssColorOverridden = false + this._openColorControl = null + this._copied = false + } + + private readonly _handleMediaChange = () => { + this._systemTheme = this._media?.matches ? 'dark' : 'light' + this._syncThemeColors() + } + + private readonly _handleDocumentPointerDown = (event: PointerEvent) => { + if (!this._openColorControl) return + const insideCurrentControl = event.composedPath().some((node) => ( + node instanceof HTMLElement + && (node.dataset.colorTrigger === this._openColorControl + || node.dataset.colorPopover === this._openColorControl) + )) + if (!insideCurrentControl) this._openColorControl = null + } + + connectedCallback(): void { + super.connectedCallback() + void defineColorElements() + this._media = globalThis.matchMedia?.('(prefers-color-scheme: dark)') + this._systemTheme = this._media?.matches ? 'dark' : 'light' + this._media?.addEventListener('change', this._handleMediaChange) + globalThis.document?.addEventListener('pointerdown', this._handleDocumentPointerDown, true) + } + + disconnectedCallback(): void { + this._media?.removeEventListener('change', this._handleMediaChange) + globalThis.document?.removeEventListener('pointerdown', this._handleDocumentPointerDown, true) + if (this._copyTimeout) clearTimeout(this._copyTimeout) + super.disconnectedCallback() + } + + protected updated(changed: PropertyValues): void { + if (changed.has('sources') || changed.has('manifestUrl') || changed.has('spriteUrl')) { + void this._loadSources() + } + if (changed.has('colorTheme')) this._syncThemeColors() + } + + private get _resolvedColorTheme(): 'light' | 'dark' { + return this.colorTheme === 'auto' ? this._systemTheme : this.colorTheme + } + + private get _themeColor(): string { + return this._resolvedColorTheme === 'dark' ? '#e5e5e5' : '#1a1a1a' + } + + private _effectiveSources(): readonly SpriteViewerSource[] { + const configured = sourceArray(this.sources) + if (configured.length > 0) return configured + if (this.manifestUrl && this.spriteUrl) { + return [{ manifestUrl: this.manifestUrl, spriteUrl: this.spriteUrl }] + } + return [] + } + + private async _loadSources(): Promise { + const version = ++this._loadVersion + const sources = this._effectiveSources() + const direct: SpriteViewerManifest[] = [] + const asynchronous: SpriteViewerSource[] = [] + const errors: string[] = [] + + if (this.manifestUrl && !this.spriteUrl && sourceArray(this.sources).length === 0) { + errors.push('The sprite-url attribute is required with manifest-url.') + } + + for (const source of sources) { + if (typeof source === 'function' || isRemoteSource(source)) { + asynchronous.push(source) + continue + } + try { + direct.push(normalizeSpriteViewerManifest(source)) + } catch (error) { + errors.push(error instanceof Error ? error.message : String(error)) + } + } + + this._manifests = direct.sort(compareManifests) + this._errors = errors + this._loading = asynchronous.length > 0 + this._selected = null + + if (asynchronous.length === 0) return + const results = await Promise.allSettled(asynchronous.map(resolveViewerSource)) + if (version !== this._loadVersion) return + + const loaded: SpriteViewerManifest[] = [] + for (const result of results) { + if (result.status === 'fulfilled') loaded.push(result.value) + else errors.push(result.reason instanceof Error ? result.reason.message : String(result.reason)) + } + this._manifests = [...direct, ...loaded].sort(compareManifests) + this._errors = [...errors] + this._loading = false + } + + private _syncThemeColors(): void { + if (!this._selected) return + this._colors = { + ...initialColors(this._selected.icon, this._themeColor), + ...this._colorOverrides, + } + if (!this._cssColorOverridden) this._cssColor = this._themeColor + } + + private _toggleTheme(): void { + const theme: Exclude = this._resolvedColorTheme === 'dark' ? 'light' : 'dark' + if (!this.themeControlled) this.colorTheme = theme + this.dispatchEvent(new CustomEvent('color-theme-change', { + bubbles: true, + composed: true, + detail: { theme }, + })) + } + + private async _selectIcon(manifest: SpriteViewerManifest, icon: SpriteViewerManifestIcon): Promise { + const key = `${manifest.name}:${manifest.spriteUrl}:${icon.id}` + this._selected = { key, manifest, icon } + this._activeTab = tabsForManifest(manifest)[0]?.id ?? 'svg' + this._colorOverrides = {} + this._colors = initialColors(icon, this._themeColor) + this._cssColor = this._themeColor + this._cssColorOverridden = false + this._openColorControl = null + this._copied = false + await this.updateComplete + const dialog = this.renderRoot.querySelector('dialog') + if (dialog && !dialog.open) dialog.showModal() + } + + private _closeDialog(): void { + const dialog = this.renderRoot.querySelector('dialog') + if (dialog?.open) dialog.close() + this._selected = null + this._openColorControl = null + } + + private _handleBackdropClick(event: MouseEvent): void { + if (!(event.currentTarget instanceof HTMLDialogElement) || event.target !== event.currentTarget) return + const bounds = event.currentTarget.getBoundingClientRect() + const outside = event.clientX < bounds.left + || event.clientX > bounds.right + || event.clientY < bounds.top + || event.clientY > bounds.bottom + if (outside) this._closeDialog() + } + + private _handleTabKeyDown(event: KeyboardEvent, tabIndex: number): void { + if (!this._selected) return + const tabs = tabsForManifest(this._selected.manifest) + let nextIndex: number | null = null + if (event.key === 'ArrowRight') nextIndex = (tabIndex + 1) % tabs.length + if (event.key === 'ArrowLeft') nextIndex = (tabIndex - 1 + tabs.length) % tabs.length + if (event.key === 'Home') nextIndex = 0 + if (event.key === 'End') nextIndex = tabs.length - 1 + if (nextIndex === null) return + + event.preventDefault() + this._activeTab = tabs[nextIndex].id + const buttons = this.renderRoot.querySelectorAll('[role="tab"]') + void this.updateComplete.then(() => buttons[nextIndex]?.focus()) + } + + private _handleColorChange(variable: string, color: string): void { + const normalized = normalizeHexColor(color) + this._colors = { ...this._colors, [variable]: normalized } + this._colorOverrides = { ...this._colorOverrides, [variable]: normalized } + } + + private _handleCssColorChange(color: string): void { + this._cssColor = normalizeHexColor(color) + this._cssColorOverridden = true + } + + private async _copyCode(code: string): Promise { + if (!globalThis.navigator?.clipboard) return + try { + await globalThis.navigator.clipboard.writeText(code) + this._copied = true + if (this._copyTimeout) clearTimeout(this._copyTimeout) + this._copyTimeout = setTimeout(() => { this._copied = false }, 1500) + } catch { + this._copied = false + } + } + + private _renderColorControl(label: string, value: string, key: string, onChange: (value: string) => void): TemplateResult { + const open = this._openColorControl === key + const popoverId = `color-${key.replace(/[^a-zA-Z0-9_-]/g, '-')}` + return html` +
+ + ${label} + ${open ? html` + + ` : nothing} +
+ ` + } + + private _renderDialogPreview(manifest: SpriteViewerManifest, icon: SpriteViewerManifestIcon): TemplateResult { + const href = `${manifest.spriteUrl}#${icon.id}` + if (this._activeTab === 'img') { + return html`${icon.name}` + } + if (this._activeTab === 'css') { + return html` + + ` + } + return html` + + + + ` + } + + private _renderDialog(): TemplateResult | typeof nothing { + if (!this._selected) return nothing + const { manifest, icon } = this._selected + const tabs = tabsForManifest(manifest) + const activeTab = tabs.some((tab) => tab.id === this._activeTab) ? this._activeTab : tabs[0].id + const code = generateViewerCode({ + manifest, + icon, + tab: activeTab, + colorOverrides: this._colorOverrides, + cssColor: this._cssColor, + }) + const dimensions = viewBoxSize(icon.viewBox) + const colorsVisible = activeTab === 'react' || activeTab === 'vue' || activeTab === 'svg' + + return html` + { event.preventDefault(); this._closeDialog() }} + @click=${this._handleBackdropClick} + > +
+ + +
+
+ ${this._renderDialogPreview(manifest, icon)} +
+
+ +
+

${icon.name}

+ ${dimensions ? html`${dimensions}` : nothing} +
+

${manifest.name} · ${manifest.format} · ${manifest.target}

+ +
+ ${colorsVisible && icon.colors.length > 0 ? html` +

+ Цвета применяются к превью через CSS-переменные и попадут в пример кода. +

+

CSS Variables

+ ${icon.colors.map(({ variable, fallback }) => this._renderColorControl( + `${variable}: ${fallback}`, + this._colors[variable], + variable, + (color) => this._handleColorChange(variable, color), + ))} + ` : nothing} + ${colorsVisible && icon.colors.length === 0 ? html` +

У иконки нет настраиваемых цветовых переменных.

+ ` : nothing} + ${activeTab === 'img' ? html` +

+ IMG изолирует SVG: CSS-переменные и currentColor внутрь изображения не передаются. +

+ ` : nothing} + ${activeTab === 'css' ? html` +

+ CSS mask отображает иконку одним цветом через background-color. +

+ ${this._renderColorControl( + 'background-color', + this._cssColor, + 'background-color', + (color) => this._handleCssColorChange(color), + )} + ` : nothing} +
+ +
+ ${tabs.map((tab, tabIndex) => html` + + `)} +
+ +
+
${unsafeHTML(highlightViewerCode(code.code, code.language))}
+ +
+
+
+ ` + } + + protected render(): TemplateResult { + const normalizedQuery = this._query.trim().toLowerCase() + const visibleGroups = this._manifests + .map((manifest) => ({ + manifest, + icons: manifest.icons.filter((icon) => ( + normalizedQuery === '' + || icon.name.toLowerCase().includes(normalizedQuery) + || manifest.name.toLowerCase().includes(normalizedQuery) + )), + })) + .filter((group) => group.icons.length > 0) + const totalIcons = this._manifests.reduce((total, manifest) => total + manifest.iconCount, 0) + const visibleIcons = visibleGroups.reduce((total, group) => total + group.icons.length, 0) + + return html` +
+
+

${this.viewerTitle}

+ + ${countLabel(this._manifests.length, ['спрайт', 'спрайта', 'спрайтов'])} + · ${countLabel(totalIcons, ['иконка', 'иконки', 'иконок'])} + ${normalizedQuery ? ` · найдено ${visibleIcons}` : nothing} + +
+ { this._query = (event.currentTarget as HTMLInputElement).value }} + placeholder="Найти иконку" + aria-label="Поиск иконок" + > + ${this.showThemeToggle ? html` + + ` : nothing} +
+
+ + ${this._errors.length > 0 ? html` + + ` : nothing} + + ${visibleGroups.map(({ manifest, icons }) => html` +
+
+

${manifest.name}

+ ${manifest.format} + ${icons.length} + ${manifest.description ? html` +

${manifest.description}

+ ` : nothing} +
+
+ ${icons.map((icon) => html` + + `)} +
+
+ `)} + + ${visibleGroups.length === 0 && (!this._loading || this._manifests.length > 0) ? html` +
+ ${this._manifests.length === 0 ? 'Спрайты не подключены' : 'Иконки не найдены'} +
+ ` : nothing} + ${this._loading && this._manifests.length === 0 ? html` +
Загрузка спрайтов...
+ ` : nothing} + + + + ${this._renderDialog()} +
+ ` + } +} + +export function defineSpriteViewerElement(): void { + if (!customElements.get(ELEMENT_NAME)) customElements.define(ELEMENT_NAME, GromlabSpriteViewerElement) +} diff --git a/src/viewer/source.ts b/src/viewer/source.ts new file mode 100644 index 0000000..289d31f --- /dev/null +++ b/src/viewer/source.ts @@ -0,0 +1,107 @@ +import type { + SpriteViewerManifest, + SpriteViewerManifestIcon, + SpriteViewerManifestLoader, + SpriteViewerRemoteSource, + SpriteViewerSource, + SpriteViewerSources, +} from './types.js' + +const manifestLoaderCache = new WeakMap>() + +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === 'object' +} + +function isManifestIcon(value: unknown): value is SpriteViewerManifestIcon { + if (!isRecord(value)) return false + return typeof value.name === 'string' + && typeof value.id === 'string' + && (value.viewBox === null || typeof value.viewBox === 'string') + && Array.isArray(value.colors) +} + +export function isSpriteViewerManifest(value: unknown): value is SpriteViewerManifest { + if (!isRecord(value)) return false + return value.schemaVersion === 1 + && value.generator === '@gromlab/svg-sprites' + && typeof value.name === 'string' + && typeof value.target === 'string' + && (value.format === 'stack' || value.format === 'symbol') + && typeof value.iconCount === 'number' + && typeof value.spriteUrl === 'string' + && Array.isArray(value.icons) + && value.icons.every(isManifestIcon) +} + +function isManifestData(value: unknown): value is Omit { + return isRecord(value) + && value.schemaVersion === 1 + && value.generator === '@gromlab/svg-sprites' + && typeof value.name === 'string' + && typeof value.target === 'string' + && (value.format === 'stack' || value.format === 'symbol') + && typeof value.iconCount === 'number' + && Array.isArray(value.icons) + && value.icons.every(isManifestIcon) +} + +function manifestCandidate(value: unknown): unknown { + if (!isRecord(value)) return value + if (isSpriteViewerManifest(value) || isManifestData(value)) return value + return value.default ?? value.spriteManifest ?? value +} + +export function normalizeSpriteViewerManifest(value: unknown, spriteUrl?: string): SpriteViewerManifest { + const candidate = manifestCandidate(value) + if (isSpriteViewerManifest(candidate)) return candidate + if (spriteUrl && isManifestData(candidate)) return { ...candidate, spriteUrl } + throw new Error('The loaded source does not contain a valid SVG sprite manifest.') +} + +export function sourceArray(sources: SpriteViewerSources): readonly SpriteViewerSource[] { + return Array.isArray(sources) + ? sources + : Object.values(sources as Readonly>) +} + +export function isRemoteSource(source: SpriteViewerSource): source is SpriteViewerRemoteSource { + if (!isRecord(source)) return false + const candidate = source as Record + return typeof candidate.manifestUrl === 'string' + && typeof candidate.spriteUrl === 'string' +} + +export function directManifests(sources: SpriteViewerSources): SpriteViewerManifest[] { + return sourceArray(sources) + .filter((source) => typeof source !== 'function' && !isRemoteSource(source)) + .map((source) => normalizeSpriteViewerManifest(source)) + .sort(compareManifests) +} + +export function compareManifests(left: SpriteViewerManifest, right: SpriteViewerManifest): number { + return left.name < right.name ? -1 : left.name > right.name ? 1 : 0 +} + +async function loadRemoteSource(source: SpriteViewerRemoteSource): Promise { + const response = await fetch(source.manifestUrl) + if (!response.ok) { + throw new Error(`Cannot load SVG sprite manifest: ${response.status} ${response.statusText}`) + } + return normalizeSpriteViewerManifest(await response.json(), source.spriteUrl) +} + +export async function resolveViewerSource(source: SpriteViewerSource): Promise { + if (isRemoteSource(source)) return loadRemoteSource(source) + if (typeof source !== 'function') return normalizeSpriteViewerManifest(source) + + const cached = manifestLoaderCache.get(source) + if (cached) return cached + + const pending = Promise.resolve().then(source).then((value) => normalizeSpriteViewerManifest(value)) + manifestLoaderCache.set(source, pending) + void pending.catch(() => { + if (manifestLoaderCache.get(source) === pending) manifestLoaderCache.delete(source) + }) + return pending +} diff --git a/src/react/sprite-viewer-styles.ts b/src/viewer/styles.ts similarity index 95% rename from src/react/sprite-viewer-styles.ts rename to src/viewer/styles.ts index f51d4af..c00b5cd 100644 --- a/src/react/sprite-viewer-styles.ts +++ b/src/viewer/styles.ts @@ -1,4 +1,5 @@ export const SPRITE_VIEWER_STYLES = ` +:host { display: block; } .gromlab-sprite-viewer { --sv-bg: #f0f0f3; --sv-text: #1a1a1a; @@ -271,9 +272,10 @@ export const SPRITE_VIEWER_STYLES = ` border-radius: 8px; box-shadow: 0 8px 28px rgba(0, 0, 0, .22); } -.gromlab-sprite-viewer__color-popover .react-colorful { width: 198px; max-width: 100%; height: 160px; } -.gromlab-sprite-viewer__hex-input { display: block; width: 100%; height: 32px; margin-top: 8px; padding: 0 8px; color: var(--sv-text); text-align: center; background: var(--sv-card); border: 1px solid var(--sv-border); border-radius: 4px; outline: none; font: 12px/1 ui-monospace, "SFMono-Regular", Consolas, monospace; } -.gromlab-sprite-viewer__hex-input:focus { border-color: var(--sv-accent); } +.gromlab-sprite-viewer__color-picker { display: flex; flex-direction: column; width: 198px; max-width: 100%; height: 160px; } +.gromlab-sprite-viewer__hex-input { display: block; width: 100%; margin-top: 8px; } +.gromlab-sprite-viewer__hex-input::part(input) { box-sizing: border-box; display: block; width: 100%; height: 32px; padding: 0 8px; color: var(--sv-text); text-align: center; background: var(--sv-card); border: 1px solid var(--sv-border); border-radius: 4px; outline: none; font: 12px/1 ui-monospace, "SFMono-Regular", Consolas, monospace; } +.gromlab-sprite-viewer__hex-input::part(input):focus { border-color: var(--sv-accent); } .gromlab-sprite-viewer__tabs { display: flex; overflow-x: auto; margin-bottom: 12px; border-bottom: 1px solid var(--sv-border); } .gromlab-sprite-viewer__tab { flex: 0 0 auto; padding: 8px 16px; color: var(--sv-muted); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; font-size: 12px; font-weight: 600; } .gromlab-sprite-viewer__tab[aria-selected="true"] { color: var(--sv-accent); border-bottom-color: var(--sv-accent); } diff --git a/src/viewer/types.ts b/src/viewer/types.ts new file mode 100644 index 0000000..1c2daae --- /dev/null +++ b/src/viewer/types.ts @@ -0,0 +1,68 @@ +export type SpriteViewerColorTheme = 'auto' | 'light' | 'dark' + +export type SpriteViewerManifestColor = { + variable: `--icon-color-${number}` + fallback: string +} + +export type SpriteViewerManifestIcon = { + name: string + id: string + viewBox: string | null + colors: readonly SpriteViewerManifestColor[] +} + +export type SpriteViewerManifestUsage = { + framework: 'react' | 'vue' + componentName: string +} + +/** Нормализованный manifest, который может отобразить framework-neutral Viewer. */ +export type SpriteViewerManifest = { + schemaVersion: 1 + generator: '@gromlab/svg-sprites' + name: string + description?: string + mode?: string + target: string + format: 'stack' | 'symbol' + iconCount: number + spriteUrl: string + icons: readonly SpriteViewerManifestIcon[] + /** Текущее поле React/Next manifests. */ + componentName?: string + /** Расширяемое описание framework-specific примера. */ + usage?: SpriteViewerManifestUsage +} + +export type SpriteViewerManifestModule = { + default?: SpriteViewerManifest + spriteManifest?: SpriteViewerManifest +} + +export type SpriteViewerManifestLoader = () => Promise + +export type SpriteViewerRemoteSource = { + manifestUrl: string + spriteUrl: string +} + +export type SpriteViewerSource = + | SpriteViewerManifest + | SpriteViewerManifestModule + | SpriteViewerManifestLoader + | SpriteViewerRemoteSource + +export type SpriteViewerSources = + | readonly SpriteViewerSource[] + | Readonly> + +export type SpriteViewerElement = HTMLElement & { + sources: SpriteViewerSources + viewerTitle: string + colorTheme: SpriteViewerColorTheme + themeControlled: boolean + showThemeToggle: boolean + manifestUrl?: string + spriteUrl?: string +} diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..cbcc1fb --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} \ No newline at end of file diff --git a/test/react-viewer-dom.test.mjs b/test/react-viewer-dom.test.mjs index bbd6952..e19b621 100644 --- a/test/react-viewer-dom.test.mjs +++ b/test/react-viewer-dom.test.mjs @@ -11,6 +11,7 @@ const manifest = { name: 'controls', description: 'Control icons', componentName: 'ControlsIcon', + mode: 'react@vite', target: 'vite', format: 'stack', iconCount: 1, @@ -25,7 +26,7 @@ const manifest = { ], } -test('SpriteViewer opens an icon dialog with code tabs and copy action', async (context) => { +test('SpriteViewer bridges React to the interactive Viewer Web Component', async (context) => { const dom = new JSDOM('
', { url: 'https://example.test/' }) const originals = new Map() const mediaListeners = new Set() @@ -45,10 +46,20 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( self: dom.window, navigator: dom.window.navigator, Node: dom.window.Node, + Element: dom.window.Element, + Document: dom.window.Document, + DocumentFragment: dom.window.DocumentFragment, Event: dom.window.Event, + CustomEvent: dom.window.CustomEvent, HTMLElement: dom.window.HTMLElement, HTMLDialogElement: dom.window.HTMLDialogElement, + HTMLInputElement: dom.window.HTMLInputElement, + KeyboardEvent: dom.window.KeyboardEvent, MouseEvent: dom.window.MouseEvent, + ShadowRoot: dom.window.ShadowRoot, + CSSStyleSheet: dom.window.CSSStyleSheet, + MutationObserver: dom.window.MutationObserver, + customElements: dom.window.customElements, matchMedia, getComputedStyle: dom.window.getComputedStyle.bind(dom.window), IS_REACT_ACT_ENVIRONMENT: true, @@ -64,8 +75,8 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( configurable: true, value: { writeText: async (value) => { clipboardWrites.push(value) } }, }) - dom.window.HTMLDialogElement.prototype.showModal = function showModal() { this.open = true } - dom.window.HTMLDialogElement.prototype.close = function close() { this.open = false } + dom.window.HTMLDialogElement.prototype.showModal = function showModal() { this.setAttribute('open', '') } + dom.window.HTMLDialogElement.prototype.close = function close() { this.removeAttribute('open') } const reactModule = await import('react') const { act: domAct } = await import('react-dom/test-utils') @@ -74,6 +85,25 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( const container = dom.window.document.querySelector('#root') const root = createRoot(container) + async function viewerRoot() { + await act(async () => { + await dom.window.customElements.whenDefined('gromlab-sprite-viewer') + await Promise.resolve() + await new Promise((resolve) => setTimeout(resolve, 0)) + }) + const host = container.querySelector('gromlab-sprite-viewer') + assert.ok(host) + await host.updateComplete + return { host, shadow: host.shadowRoot } + } + + async function settle(host) { + await act(async () => { + await host.updateComplete + await Promise.resolve() + }) + } + context.after(async () => { await act(async () => root.unmount()) dom.window.close() @@ -83,98 +113,79 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( } }) - await act(async () => root.render(createElement(SpriteViewer, { sources: [manifest] }))) - assert.equal(container.querySelector('[data-sprite-viewer]').dataset.theme, undefined) + await act(async () => root.render(createElement(SpriteViewer, { sources: [manifest], title: 'Icon catalog' }))) + let { host, shadow } = await viewerRoot() + assert.equal(host.viewerTitle, 'Icon catalog') + assert.equal(shadow.querySelector('[data-sprite-viewer]').dataset.theme, undefined) + assert.match(shadow.textContent, /Icon catalog/) + assert.match(shadow.textContent, /1 спрайт\s*· 1 иконка/) - const card = container.querySelector('[data-icon-name="check"]') + const card = shadow.querySelector('[data-icon-name="check"]') assert.ok(card) - await act(async () => card.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) + card.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true, composed: true })) + await settle(host) - const dialog = container.querySelector('dialog') + let dialog = shadow.querySelector('dialog') assert.ok(dialog) assert.equal(dialog.open, true) - assert.match(dialog.textContent, /check/) assert.match(dialog.textContent, /16 × 16/) assert.match(dialog.textContent, /ControlsIcon/) const reactTab = [...dialog.querySelectorAll('[role="tab"]')] .find((element) => element.textContent === 'React') - await act(async () => reactTab.dispatchEvent(new dom.window.KeyboardEvent('keydown', { - bubbles: true, - key: 'ArrowRight', - }))) + reactTab.dispatchEvent(new dom.window.KeyboardEvent('keydown', { bubbles: true, key: 'ArrowRight' })) + await settle(host) assert.equal(dialog.querySelector('[role="tab"][aria-selected="true"]').textContent, 'SVG') const colorSwatch = dialog.querySelector('[aria-label^="Изменить цвет --icon-color-1"]') assert.ok(colorSwatch) - assert.equal(dialog.querySelector('.gromlab-sprite-viewer__hex-input'), null) - await act(async () => { - prefersDark = true - for (const listener of mediaListeners) listener({ matches: true, media: darkModeMedia.media }) - }) + prefersDark = true + for (const listener of mediaListeners) listener({ matches: true, media: darkModeMedia.media }) + await settle(host) assert.equal(colorSwatch.style.backgroundColor, 'rgb(229, 229, 229)') - await act(async () => colorSwatch.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) - assert.ok(dialog.querySelector('.react-colorful')) - const colorRow = colorSwatch.closest('.gromlab-sprite-viewer__color-row') - await act(async () => colorRow.dispatchEvent(new dom.window.Event('pointerdown', { bubbles: true }))) - assert.equal(dialog.querySelector('.react-colorful'), null) - await act(async () => colorRow.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) - assert.equal(dialog.querySelector('.react-colorful'), null) - - await act(async () => colorSwatch.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) - const colorPopover = dialog.querySelector('.gromlab-sprite-viewer__color-popover') - await act(async () => colorPopover.querySelector('.react-colorful').dispatchEvent( - new dom.window.Event('pointerdown', { bubbles: true }), - )) - assert.ok(dialog.querySelector('.react-colorful')) - await act(async () => colorPopover.dispatchEvent(new dom.window.KeyboardEvent('keydown', { - bubbles: true, - key: 'Escape', - }))) - assert.equal(dialog.querySelector('.react-colorful'), null) - assert.ok(container.querySelector('dialog')) - - await act(async () => colorSwatch.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) - const colorInput = dialog.querySelector('.gromlab-sprite-viewer__hex-input') - assert.ok(colorInput) - const setInputValue = Object.getOwnPropertyDescriptor(dom.window.HTMLInputElement.prototype, 'value').set - setInputValue.call(colorInput, '#ff0000') - await act(async () => colorInput.dispatchEvent(new dom.window.Event('input', { bubbles: true }))) + colorSwatch.click() + await settle(host) + await dom.window.customElements.whenDefined('gromlab-hex-input') + const colorInputElement = dialog.querySelector('gromlab-hex-input') + assert.ok(colorInputElement) + const colorInput = colorInputElement.shadowRoot.querySelector('input') + colorInput.value = '#ff0000' + colorInput.dispatchEvent(new dom.window.Event('input', { bubbles: true, composed: true })) + await settle(host) assert.match(dialog.querySelector('[role="tabpanel"]').textContent, /--icon-color-1/) - assert.doesNotMatch(dialog.querySelector('[role="tabpanel"]').textContent, /React\.CSSProperties/) - await act(async () => { - prefersDark = false - for (const listener of mediaListeners) listener({ matches: false, media: darkModeMedia.media }) - }) assert.equal(colorSwatch.style.backgroundColor, 'rgb(255, 0, 0)') const cssTab = [...dialog.querySelectorAll('[role="tab"]')] .find((element) => element.textContent === 'CSS') - assert.ok(cssTab) - await act(async () => cssTab.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) + cssTab.click() + await settle(host) assert.match(dialog.querySelector('[role="tabpanel"]').textContent, /mask:/) const copyButton = [...dialog.querySelectorAll('button')] .find((element) => element.textContent === 'Копировать') - assert.ok(copyButton) - await act(async () => copyButton.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) + copyButton.click() + await settle(host) assert.equal(clipboardWrites.length, 1) assert.match(clipboardWrites[0], /background-color/) - await act(async () => dialog.dispatchEvent(new dom.window.Event('cancel', { cancelable: true }))) - assert.equal(container.querySelector('dialog'), null) + dialog.dispatchEvent(new dom.window.Event('cancel', { cancelable: true })) + await settle(host) + assert.equal(shadow.querySelector('dialog'), null) - const themeButton = container.querySelector('[aria-label="Переключить тему"]') - await act(async () => themeButton.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) - assert.equal(container.querySelector('[data-sprite-viewer]').dataset.theme, 'dark') + const themeButton = shadow.querySelector('[aria-label="Переключить тему"]') + themeButton.click() + await settle(host) + assert.equal(shadow.querySelector('[data-sprite-viewer]').dataset.theme, 'light') await act(async () => root.render(createElement(SpriteViewer, { sources: [manifest], colorTheme: 'light', }))) - assert.equal(container.querySelector('[data-sprite-viewer]').dataset.theme, 'light') - assert.equal(container.querySelector('[aria-label="Переключить тему"]'), null) + ;({ host, shadow } = await viewerRoot()) + await settle(host) + assert.equal(shadow.querySelector('[data-sprite-viewer]').dataset.theme, 'light') + assert.equal(shadow.querySelector('[aria-label="Переключить тему"]'), null) const controlledChanges = [] await act(async () => root.render(createElement(SpriteViewer, { @@ -182,18 +193,22 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( colorTheme: 'light', onColorThemeChange: (theme) => controlledChanges.push(theme), }))) - const controlledThemeButton = container.querySelector('[aria-label="Переключить тему"]') - await act(async () => controlledThemeButton.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) + ;({ host, shadow } = await viewerRoot()) + await settle(host) + shadow.querySelector('[aria-label="Переключить тему"]').click() + await settle(host) assert.deepEqual(controlledChanges, ['dark']) - assert.equal(container.querySelector('[data-sprite-viewer]').dataset.theme, 'light') + assert.equal(shadow.querySelector('[data-sprite-viewer]').dataset.theme, 'light') await act(async () => root.render(createElement(SpriteViewer, { sources: [{ ...manifest, format: 'symbol' }], }))) - const symbolCard = container.querySelector('[data-icon-name="check"]') - await act(async () => symbolCard.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true }))) + ;({ host, shadow } = await viewerRoot()) + await settle(host) + shadow.querySelector('[data-icon-name="check"]').click() + await settle(host) assert.deepEqual( - [...container.querySelectorAll('[role="tab"]')].map((element) => element.textContent), + [...shadow.querySelectorAll('[role="tab"]')].map((element) => element.textContent), ['React', 'SVG'], ) @@ -207,6 +222,12 @@ test('SpriteViewer opens an icon dialog with code tabs and copy action', async ( null, createElement(SpriteViewer, { sources: [loader] }), ))) + ;({ host, shadow } = await viewerRoot()) + await act(async () => { + await Promise.resolve() + await Promise.resolve() + await host.updateComplete + }) assert.equal(loaderCalls, 1) - assert.match(container.textContent, /controls/) + assert.match(shadow.textContent, /controls/) }) diff --git a/test/react-viewer.test.mjs b/test/react-viewer.test.mjs index fbbfcb6..d711a7f 100644 --- a/test/react-viewer.test.mjs +++ b/test/react-viewer.test.mjs @@ -40,14 +40,9 @@ test('SpriteViewer renders direct sprite manifests', () => { title: 'Icon catalog', })) - assert.match(markup, /Icon catalog/) - assert.match(markup, /controls/) - assert.match(markup, /Control icons/) - assert.match(markup, /check/) - assert.match(markup, /\/assets\/controls\.svg#check/) - assert.match(markup, /1 спрайт · 1 иконка/) - assert.match(markup, /gromlab-sprite-viewer__icon-wrap/) - assert.match(markup, /Открыть check/) + assert.match(markup, /^ { @@ -57,7 +52,7 @@ test('SpriteViewer accepts a loader record from import.meta.glob', () => { }, })) - assert.match(markup, /Загрузка спрайтов/) + assert.match(markup, /^ { @@ -66,6 +61,6 @@ test('SpriteViewer supports an externally controlled color theme', () => { colorTheme: 'dark', })) - assert.match(markup, /^]*data-theme="dark"/) - assert.doesNotMatch(markup, /]*aria-label="Переключить тему"/) + assert.match(markup, /^ Date: Tue, 14 Jul 2026 16:11:39 +0300 Subject: [PATCH 07/11] sync --- AGENTS.md | 13 +- FEATURES.md | 8 +- README.md | 57 +- README_RU.md | 57 +- docs/en/README.md | 29 + docs/en/guides/README.md | 11 + docs/en/guides/next-app-turbopack.md | 153 +++++ docs/en/guides/next-app-webpack.md | 153 +++++ docs/en/guides/next-pages-turbopack.md | 140 ++++ docs/en/guides/next-pages-webpack.md | 140 ++++ docs/en/guides/react-vite.md | 148 ++++ docs/en/guides/react-webpack.md | 156 +++++ docs/en/guides/standalone-vite.md | 156 +++++ docs/en/guides/standalone-webpack.md | 143 ++++ docs/en/guides/standalone.md | 171 +++++ docs/en/next-app.md | 115 +--- docs/en/next-pages.md | 107 +-- docs/en/programmatic-api.md | 148 +--- docs/en/react-vite.md | 126 +--- docs/en/react-webpack.md | 130 +--- docs/en/reference.md | 556 +-------------- docs/en/reference/README.md | 4 + docs/en/reference/programmatic-api.md | 147 ++++ docs/en/reference/technical.md | 632 ++++++++++++++++++ docs/ru/README.md | 29 + docs/ru/guides/README.md | 11 + docs/ru/guides/next-app-turbopack.md | 153 +++++ docs/ru/guides/next-app-webpack.md | 151 +++++ docs/ru/guides/next-pages-turbopack.md | 140 ++++ docs/ru/guides/next-pages-webpack.md | 140 ++++ docs/ru/guides/react-vite.md | 141 ++++ docs/ru/guides/react-webpack.md | 141 ++++ docs/ru/guides/standalone-vite.md | 151 +++++ docs/ru/guides/standalone-webpack.md | 145 ++++ docs/ru/guides/standalone.md | 108 +++ docs/ru/next-app.md | 115 +--- docs/ru/next-pages.md | 107 +-- docs/ru/programmatic-api.md | 148 +--- docs/ru/react-vite.md | 126 +--- docs/ru/react-webpack.md | 130 +--- docs/ru/reference.md | 556 +-------------- docs/ru/reference/README.md | 4 + docs/ru/reference/programmatic-api.md | 147 ++++ docs/ru/reference/technical.md | 631 +++++++++++++++++ .../src/sprite/svg-sprite.config.ts | 2 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/generated-contract.typecheck.ts | 9 + integration/apps/standalone-vite/src/main.ts | 13 +- .../apps/standalone-vite/src/sprite/index.ts | 4 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../src/generated-contract.typecheck.ts | 9 + .../apps/standalone-webpack/src/main.ts | 13 +- .../standalone-webpack/src/sprite/index.ts | 4 +- .../src/sprite/svg-sprite.config.ts | 2 +- .../apps/standalone/src/sprite/.gitignore | 2 - .../src/sprite/svg-sprite.config.json | 2 +- integration/package-lock.json | 3 +- integration/tests/apps.spec.ts | 6 + package-lock.json | 12 +- package.json | 7 +- skills/README.md | 8 +- skills/svg-sprites/skill.config.mjs | 44 +- skills/svg-sprites/src/en/SKILL.md | 7 +- .../src/en/core/00-package-overview.md | 5 +- .../src/en/core/10-mode-selection.md | 6 +- .../src/en/core/20-project-inspection.md | 18 +- .../src/en/core/30-react-next-setup.md | 37 +- .../src/en/core/40-generated-contract.md | 15 +- .../src/en/core/50-usage-and-colors.md | 22 +- .../src/en/core/60-verification.md | 2 +- .../svg-sprites/src/en/core/70-diagnostics.md | 7 +- .../src/en/references/complex-svg.md | 4 +- .../svg-sprites/src/en/references/next-app.md | 149 ----- .../src/en/references/next-pages.md | 138 ---- .../src/en/references/programmatic-api.md | 11 +- .../src/en/references/react-vite.md | 166 ----- .../src/en/references/react-webpack.md | 152 ----- skills/svg-sprites/src/ru/SKILL.md | 7 +- .../src/ru/core/00-package-overview.md | 5 +- .../src/ru/core/10-mode-selection.md | 6 +- .../src/ru/core/20-project-inspection.md | 18 +- .../src/ru/core/30-react-next-setup.md | 37 +- .../src/ru/core/40-generated-contract.md | 15 +- .../src/ru/core/50-usage-and-colors.md | 22 +- .../src/ru/core/60-verification.md | 2 +- .../svg-sprites/src/ru/core/70-diagnostics.md | 7 +- .../src/ru/references/complex-svg.md | 4 +- .../svg-sprites/src/ru/references/next-app.md | 149 ----- .../src/ru/references/next-pages.md | 138 ---- .../src/ru/references/programmatic-api.md | 11 +- .../src/ru/references/react-vite.md | 166 ----- .../src/ru/references/react-webpack.md | 152 ----- src/cli/parse-args.ts | 18 +- src/config.ts | 43 +- src/core/mode-adapter.ts | 3 +- src/core/output-writer.ts | 339 ++++------ src/core/prepare-sprite.ts | 3 +- src/generate.ts | 3 +- src/modes/next-app-turbopack/adapter.ts | 2 - src/modes/next-app-turbopack/output.ts | 32 +- src/modes/next-app-webpack/adapter.ts | 1 - src/modes/next-app-webpack/output.ts | 32 +- src/modes/next-pages-turbopack/adapter.ts | 1 - src/modes/next-pages-turbopack/output.ts | 32 +- src/modes/next-pages-webpack/adapter.ts | 1 - src/modes/next-pages-webpack/output.ts | 32 +- src/modes/react-vite/adapter.ts | 2 - src/modes/react-vite/output.ts | 25 +- src/modes/react-webpack/adapter.ts | 2 - src/modes/react-webpack/output.ts | 32 +- src/modes/standalone-vite/adapter.ts | 2 - src/modes/standalone-vite/output.ts | 129 +++- src/modes/standalone-webpack/adapter.ts | 2 - src/modes/standalone-webpack/output.ts | 129 +++- src/modes/standalone/adapter.ts | 3 +- src/scanner.ts | 105 ++- src/types.ts | 11 +- test/cli-input.test.mjs | 47 ++ test/documentation-guides.test.mjs | 76 +++ test/mode-contracts.test.mjs | 120 +++- test/standalone-icon-element.test.mjs | 104 +++ test/standalone-mode.test.mjs | 55 +- 126 files changed, 5670 insertions(+), 4120 deletions(-) create mode 100644 docs/en/README.md create mode 100644 docs/en/guides/README.md create mode 100644 docs/en/guides/next-app-turbopack.md create mode 100644 docs/en/guides/next-app-webpack.md create mode 100644 docs/en/guides/next-pages-turbopack.md create mode 100644 docs/en/guides/next-pages-webpack.md create mode 100644 docs/en/guides/react-vite.md create mode 100644 docs/en/guides/react-webpack.md create mode 100644 docs/en/guides/standalone-vite.md create mode 100644 docs/en/guides/standalone-webpack.md create mode 100644 docs/en/guides/standalone.md create mode 100644 docs/en/reference/README.md create mode 100644 docs/en/reference/programmatic-api.md create mode 100644 docs/en/reference/technical.md create mode 100644 docs/ru/README.md create mode 100644 docs/ru/guides/README.md create mode 100644 docs/ru/guides/next-app-turbopack.md create mode 100644 docs/ru/guides/next-app-webpack.md create mode 100644 docs/ru/guides/next-pages-turbopack.md create mode 100644 docs/ru/guides/next-pages-webpack.md create mode 100644 docs/ru/guides/react-vite.md create mode 100644 docs/ru/guides/react-webpack.md create mode 100644 docs/ru/guides/standalone-vite.md create mode 100644 docs/ru/guides/standalone-webpack.md create mode 100644 docs/ru/guides/standalone.md create mode 100644 docs/ru/reference/README.md create mode 100644 docs/ru/reference/programmatic-api.md create mode 100644 docs/ru/reference/technical.md create mode 100644 integration/apps/standalone-vite/src/generated-contract.typecheck.ts create mode 100644 integration/apps/standalone-webpack/src/generated-contract.typecheck.ts delete mode 100644 integration/apps/standalone/src/sprite/.gitignore delete mode 100644 skills/svg-sprites/src/en/references/next-app.md delete mode 100644 skills/svg-sprites/src/en/references/next-pages.md delete mode 100644 skills/svg-sprites/src/en/references/react-vite.md delete mode 100644 skills/svg-sprites/src/en/references/react-webpack.md delete mode 100644 skills/svg-sprites/src/ru/references/next-app.md delete mode 100644 skills/svg-sprites/src/ru/references/next-pages.md delete mode 100644 skills/svg-sprites/src/ru/references/react-vite.md delete mode 100644 skills/svg-sprites/src/ru/references/react-webpack.md create mode 100644 test/cli-input.test.mjs create mode 100644 test/documentation-guides.test.mjs create mode 100644 test/standalone-icon-element.test.mjs diff --git a/AGENTS.md b/AGENTS.md index 2a9acf8..2c4f586 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,7 +50,7 @@ - подготовка нейтрального compiled artifact; - protocol `ModeAdapter`/`OutputPlan`; - проверка output paths; -- atomic writer, symlink protection и ownership state; +- staged directory writer и symlink protection; - logger и базовые result types. Core не генерирует JavaScript, declarations, manifest source, CSS или framework-specific exports. Изменение общего compiler может ожидаемо изменить SVG всех modes; изменение generated source должно быть локально одному adapter. @@ -61,13 +61,11 @@ Core не генерирует JavaScript, declarations, manifest source, CSS и Runtime генерируется как ESM JavaScript. Типизация добавляется отдельными `.d.ts`; TypeScript/TSX не используется как runtime output. -Core writer владеет корневым `.gitignore` и `.svg-sprite/state.json`. State не является manifest спрайта: он хранит owner mode, contract version и список управляемых файлов. +Core writer полностью владеет каталогом `.svg-sprite` и при каждой генерации заменяет его через временный каталог с rollback при ошибке. Корневым `.gitignore` writer владеет, когда exact-mode adapter запрашивает его через `OutputPlan`. Bare `standalone` не создаёт `.gitignore`; остальные modes создают. -Sprite-level asset, icon data, manifest и facade лежат непосредственно в `.svg-sprite/`. Framework runtime группируется отдельно: React adapters используют `.svg-sprite/react/`, будущие framework adapters получают собственный framework-каталог. +Sprite-level asset, icon data, manifest и facade лежат непосредственно в `.svg-sprite/`. `standalone@vite` и `standalone@webpack` генерируют нативный icon Web Component внутри своего facade; bare `standalone` остаётся без JavaScript runtime. Framework runtime группируется отдельно: React adapters используют `.svg-sprite/react/`, будущие framework adapters получают собственный framework-каталог. -Каждый adapter имеет собственный `contractVersion`. При изменении его generated-контракта повышается только версия этого adapter. - -Adapter возвращает файлы в памяти. Только core writer проверяет paths и markers, обновляет output и записывает `state.json` последним. +Adapter возвращает файлы в памяти. Только core writer проверяет paths, полностью заменяет `.svg-sprite` и обновляет управляемый `.gitignore`. ## Зависимости @@ -98,5 +96,4 @@ mode A -X-> shared output codegen 1. Изменяйте только его каталог и mode-neutral protocol, если это действительно необходимо. 2. Не переносите output-логику в core ради устранения дублирования. 3. Не меняйте generated-контракты других adapters автоматически. -4. Повышайте `contractVersion` только изменённого adapter. -5. Проверяйте отсутствие cross-mode imports. +4. Проверяйте отсутствие cross-mode imports. diff --git a/FEATURES.md b/FEATURES.md index 9207255..d15f11c 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,6 +1,6 @@ # Иконки без лишней цены для приложения -`@gromlab/svg-sprites` превращает SVG проекта в кешируемую, типизированную систему иконок для React и Next.js. В коде остаются простые компоненты, а приложение получает преимущества спрайтов без сложной инфраструктуры. +`@gromlab/svg-sprites` превращает SVG проекта в кешируемую, типизированную систему иконок для vanilla-приложений, React и Next.js. В коде остаются простые компоненты, а приложение получает преимущества спрайтов без сложной инфраструктуры. 1. **AI-friendly из коробки** @@ -42,9 +42,9 @@ `SpriteViewer` рендерит все спрайты проекта в одном месте и показывает, какие иконки вошли в каждый набор и как они выглядят. Для каждой иконки видны созданные CSS-переменные и их fallback-цвета. Значения можно менять прямо в Viewer и сразу наблюдать результат. Здесь же доступны готовые примеры подключения через React, ``, `` и CSS. -11. **React и Next.js** +11. **Standalone, React и Next.js** - Пакет генерирует типизированные React-компоненты и поддерживает Vite, Webpack 5, Next.js App Router и Pages Router с Turbopack или Webpack. + Для vanilla-приложений с Vite или Webpack пакет генерирует нативный типизированный Web Component без runtime-зависимостей. Для React и Next.js создаётся React-компонент с поддержкой Vite, Webpack 5, App Router, Pages Router и Turbopack. 12. **Чистый Git** @@ -52,4 +52,4 @@ 13. **В production только иконки** - `@gromlab/svg-sprites` выполняет всю работу на этапе генерации и остаётся в `devDependencies`. Compiler и CLI не попадают в клиентское приложение: после сборки остаются только локальный типизированный компонент и внешний SVG-файл. + Генератор можно запускать через `npx`, не добавляя package в проект. Локальная установка нужна только для необязательного Viewer, типизации config через package или программного API. Compiler и CLI не попадают в клиентское приложение: после сборки остаются только локальный типизированный компонент и внешний SVG-файл. diff --git a/README.md b/README.md index 3ea21c9..ab4a13a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` is an SVG sprite generator for modern web applications. It combines selected SVG icons into one or more external, cacheable sprites and prepares them for use in the UI. -For React and Next.js, the package generates typed components and external SVG assets with support for Vite, Webpack 5, and Turbopack. +For vanilla applications using Vite/Webpack, the package generates a native typed Web Component; for React and Next.js, it generates a React component. In every case, the SVG remains a separate cacheable asset. ## An SVG sprite as simple as a regular SVG icon @@ -24,6 +24,20 @@ The component accepts familiar SVG attributes: dimensions, `color`, `className`, You do not have to work with the sprite directly in your application. Use it like a regular SVG icon while benefiting from a single component, autocomplete, and TypeScript validation for every name. +In `standalone@vite` and `standalone@webpack`, the same approach works without React: + +```ts +import { defineAppIconElement } from './app-icons' + +defineAppIconElement() +``` + +```html + +``` + +Bare `standalone` remains minimal and generates only an SVG asset and JSON manifest, with no JavaScript runtime. + ## AI-friendly out of the box `@gromlab/svg-sprites` is designed to work with AI agents from the start. Add the ready-made skill and ask an agent to configure, migrate, or troubleshoot the package without lengthy instructions or manual documentation research. @@ -36,12 +50,15 @@ You do not have to work with the sprite directly in your application. Use it lik The main example uses the Next.js App Router and Turbopack. -### 1. Install the package +### 1. Generate without installing the package ```bash -npm install --save-dev @gromlab/svg-sprites +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --help ``` +`npx` downloads the CLI temporarily. It does not add `@gromlab/svg-sprites` to +`package.json`, and the generated production runtime does not import the package. + ### 2. Specify the icons you need SVG files can remain in your project's existing structure: @@ -61,17 +78,15 @@ Create the sprite configuration: ```ts // src/ui/app-icons/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ +export default { mode: 'next@app/turbopack', name: 'app', - inputFiles: [ + input: [ '../../assets/icons/search.svg', '../../assets/icons/settings.svg', '../../features/profile/user.svg', ], -}) +} ``` ### 3. Add generation @@ -79,7 +94,7 @@ export default defineSpriteConfig({ ```json { "scripts": { - "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites" } @@ -233,13 +248,13 @@ The package generates low-level standalone sprites for static HTML, Vite, and We ## Clean Git history -The generator creates a local `.gitignore` that excludes generated files and keeps them from cluttering project history, pull requests, and the codebase. +Bundler and framework modes create a local `.gitignore` that excludes generated files and keeps them from cluttering project history, pull requests, and the codebase. Bare `standalone` leaves the repository policy to the application. -The repository contains the source SVG files, configuration, and `.gitignore` rule, while sprites, components, and types are regenerated locally and in CI through `prebuild`. +In bundler and framework modes, the repository contains the source SVG files, configuration, and `.gitignore` rule, while sprites, components, and types are regenerated locally and in CI through `prebuild`. ## Only icons in production -`@gromlab/svg-sprites` does its main work during generation and remains in `devDependencies`. +Generation can run entirely through `npx`, without adding the package to the project. Install it as a development dependency only when you need the Viewer, config types, or the programmatic API. Production components use only local generated code, styles, and the external SVG file. The compiler and CLI are not bundled into the client application, while `SpriteViewer` is imported separately only where a debug page is needed. @@ -249,15 +264,21 @@ This README introduces the project's capabilities and demonstrates the primary u ### Quick start -- [Next.js App Router](docs/en/next-app.md) -- [Next.js Pages Router](docs/en/next-pages.md) -- [React + Vite](docs/en/react-vite.md) -- [React + Webpack 5](docs/en/react-webpack.md) +- [Bare standalone](docs/en/guides/standalone.md) +- [Standalone + Vite](docs/en/guides/standalone-vite.md) +- [Standalone + Webpack 5](docs/en/guides/standalone-webpack.md) +- [React + Vite](docs/en/guides/react-vite.md) +- [React + Webpack 5](docs/en/guides/react-webpack.md) +- [Next.js App Router + Turbopack](docs/en/guides/next-app-turbopack.md) +- [Next.js App Router + Webpack](docs/en/guides/next-app-webpack.md) +- [Next.js Pages Router + Turbopack](docs/en/guides/next-pages-turbopack.md) +- [Next.js Pages Router + Webpack](docs/en/guides/next-pages-webpack.md) ### Technical resources -- [Technical reference](docs/en/reference.md) -- [Programmatic API](docs/en/programmatic-api.md) +- [Documentation index](docs/en/README.md) +- [Technical reference](docs/en/reference/technical.md) +- [Programmatic API](docs/en/reference/programmatic-api.md) ## License diff --git a/README_RU.md b/README_RU.md index 97ace8c..a41fcdc 100644 --- a/README_RU.md +++ b/README_RU.md @@ -6,7 +6,7 @@ `@gromlab/svg-sprites` — генератор SVG-спрайтов для современных веб-приложений. Он собирает выбранные SVG-иконки в один или несколько внешних кешируемых спрайтов и подготавливает их для использования в интерфейсе. -Для React и Next.js пакет создаёт типизированные компоненты и внешние SVG assets, поддерживая Vite, Webpack 5 и Turbopack. +Для vanilla-приложений с Vite/Webpack пакет создаёт нативный типизированный Web Component, а для React и Next.js — React-компонент. SVG во всех случаях остаётся отдельным кешируемым asset. ## SVG-спрайт так же прост, как обычная SVG-иконка @@ -24,6 +24,20 @@ В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён. +В `standalone@vite` и `standalone@webpack` тот же подход доступен без React: + +```ts +import { defineAppIconElement } from './app-icons' + +defineAppIconElement() +``` + +```html + +``` + +Bare `standalone` остаётся минимальным и генерирует только SVG asset и JSON manifest без JavaScript runtime. + ## AI-friendly из коробки `@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации. @@ -36,12 +50,15 @@ Основной пример использует Next.js App Router и Turbopack. -### 1. Установите пакет +### 1. Генерируйте без установки пакета ```bash -npm install --save-dev @gromlab/svg-sprites +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --help ``` +`npx` временно скачивает CLI, не добавляет `@gromlab/svg-sprites` в +`package.json`, а generated production runtime не импортирует package. + ### 2. Укажите нужные иконки SVG могут оставаться в существующей структуре проекта: @@ -61,17 +78,15 @@ src/ ```ts // src/ui/app-icons/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ +export default { mode: 'next@app/turbopack', name: 'app', - inputFiles: [ + input: [ '../../assets/icons/search.svg', '../../assets/icons/settings.svg', '../../features/profile/user.svg', ], -}) +} ``` ### 3. Добавьте генерацию @@ -79,7 +94,7 @@ export default defineSpriteConfig({ ```json { "scripts": { - "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites" } @@ -233,13 +248,13 @@ Bare standalone подключает Viewer через browser script и HTML el ## Чистый Git -Генератор создаёт локальный `.gitignore`, который исключает generated-файлы и не позволяет им засорять историю, pull requests и код проекта. +Bundler и framework modes создают локальный `.gitignore`, который исключает generated-файлы и не позволяет им засорять историю, pull requests и код проекта. Bare `standalone` оставляет политику репозитория приложению. -В репозитории остаются исходные SVG, конфигурация и правило `.gitignore`, а локально и в CI спрайты, компоненты и типы заново создаются через `prebuild`. +В bundler и framework modes в репозитории остаются исходные SVG, конфигурация и правило `.gitignore`, а локально и в CI спрайты, компоненты и типы заново создаются через `prebuild`. ## В production только иконки -`@gromlab/svg-sprites` выполняет основную работу на этапе генерации и остаётся в `devDependencies`. +Генерация полностью работает через `npx`, без добавления package в проект. Устанавливайте его как development dependency, только если нужны Viewer, типы конфига или программный API. Production-компоненты используют только локальный generated-код, стили и внешний SVG-файл. Compiler и CLI не попадают в клиентское приложение, а `SpriteViewer` подключается отдельно только там, где нужна debug-страница. @@ -249,15 +264,21 @@ README знакомит с возможностями проекта и пока ### Быстрый старт -- [Next.js App Router](docs/ru/next-app.md) -- [Next.js Pages Router](docs/ru/next-pages.md) -- [React + Vite](docs/ru/react-vite.md) -- [React + Webpack 5](docs/ru/react-webpack.md) +- [Bare standalone](docs/ru/guides/standalone.md) +- [Standalone + Vite](docs/ru/guides/standalone-vite.md) +- [Standalone + Webpack 5](docs/ru/guides/standalone-webpack.md) +- [React + Vite](docs/ru/guides/react-vite.md) +- [React + Webpack 5](docs/ru/guides/react-webpack.md) +- [Next.js App Router + Turbopack](docs/ru/guides/next-app-turbopack.md) +- [Next.js App Router + Webpack](docs/ru/guides/next-app-webpack.md) +- [Next.js Pages Router + Turbopack](docs/ru/guides/next-pages-turbopack.md) +- [Next.js Pages Router + Webpack](docs/ru/guides/next-pages-webpack.md) ### Технические материалы -- [Технический справочник](docs/ru/reference.md) -- [Программный API](docs/ru/programmatic-api.md) +- [Индекс документации](docs/ru/README.md) +- [Технический справочник](docs/ru/reference/technical.md) +- [Программный API](docs/ru/reference/programmatic-api.md) ## Лицензия diff --git a/docs/en/README.md b/docs/en/README.md new file mode 100644 index 0000000..23519ca --- /dev/null +++ b/docs/en/README.md @@ -0,0 +1,29 @@ +# Documentation + +Choose one exact mode guide for setup. The guides are standalone documents and +can also be used unchanged by AI skills. + +## Quick Start Guides + +| Project | Exact mode | Guide | +|---|---|---| +| Static HTML or custom publishing | `standalone` | [Bare standalone](guides/standalone.md) | +| Vanilla + Vite | `standalone@vite` | [Standalone + Vite](guides/standalone-vite.md) | +| Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) | +| React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) | +| React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) | +| Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) | +| Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) | +| Next.js Pages Router + Webpack | `next@pages/webpack` | [Pages Router + Webpack](guides/next-pages-webpack.md) | + +Every guide follows the same order: + +1. Generate the sprite through `npx` without adding the package to the project. +2. Optionally install and connect the debug Viewer. +3. Optionally type the config through the package or a local copy-paste type. + +## Reference + +- [Technical reference](reference/technical.md) +- [Programmatic API](reference/programmatic-api.md) diff --git a/docs/en/guides/README.md b/docs/en/guides/README.md new file mode 100644 index 0000000..d832d85 --- /dev/null +++ b/docs/en/guides/README.md @@ -0,0 +1,11 @@ +# Quick Start Guides + +- `standalone`: [bare standalone](standalone.md) +- `standalone@vite`: [standalone with Vite](standalone-vite.md) +- `standalone@webpack`: [standalone with Webpack](standalone-webpack.md) +- `react@vite`: [React with Vite](react-vite.md) +- `react@webpack`: [React with Webpack](react-webpack.md) +- `next@app/turbopack`: [App Router with Turbopack](next-app-turbopack.md) +- `next@app/webpack`: [App Router with Webpack](next-app-webpack.md) +- `next@pages/turbopack`: [Pages Router with Turbopack](next-pages-turbopack.md) +- `next@pages/webpack`: [Pages Router with Webpack](next-pages-webpack.md) diff --git a/docs/en/guides/next-app-turbopack.md b/docs/en/guides/next-app-turbopack.md new file mode 100644 index 0000000..aaef31a --- /dev/null +++ b/docs/en/guides/next-app-turbopack.md @@ -0,0 +1,153 @@ +# Next.js App Router Turbopack SVG Sprite Quick Start + +This guide targets the exact mode key `next@app/turbopack`: a generated typed React icon for the Next.js App Router and Turbopack. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config and source icons together: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'next@app/turbopack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Generate once per invocation and keep the exact Turbopack flags on both commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --turbopack", + "build": "npm run sprites && next build --turbopack" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +The generated icon has no `'use client'` directive and is Server Component-compatible. Import it directly in an App Router page or layout: + +```tsx +// src/app/page.tsx +import { + IconsIcon, + iconsIconNames, +} from '../ui/icons/.svg-sprite/index.js' + +export default function Page() { + return ( +
+ +

{iconsIconNames.length} icons available

+
+ ) +} +``` + +Turbopack resolves the generated `new URL('../sprite.svg', import.meta.url)` and CSS Module, emitting a separate SVG asset. Keep the mode and the `--turbopack` dev/build flags aligned. + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Viewer is interactive, so place the React bridge in a separate Client Component: + +```tsx +// src/app/icon-debug/IconsViewer.tsx +'use client' + +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../../ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export function IconsViewer() { + return +} +``` + +Render it from the route's Server Component: + +```tsx +// src/app/icon-debug/page.tsx +import { IconsViewer } from './IconsViewer' + +export default function IconDebugPage() { + return +} +``` + +Keep the route internal or development-only. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'next@app/turbopack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@app/turbopack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/next-app-webpack.md b/docs/en/guides/next-app-webpack.md new file mode 100644 index 0000000..08ac234 --- /dev/null +++ b/docs/en/guides/next-app-webpack.md @@ -0,0 +1,153 @@ +# Next.js App Router Webpack SVG Sprite Quick Start + +This guide targets the exact mode key `next@app/webpack`: a generated typed React icon for the Next.js App Router and Webpack 5. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config adjacent to its source icons: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'next@app/webpack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Generate once per invocation and keep the exact Webpack flags on both commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --webpack", + "build": "npm run sprites && next build --webpack" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +The generated icon has no `'use client'` directive and is Server Component-compatible. Import it directly in an App Router page or layout: + +```tsx +// src/app/page.tsx +import { + IconsIcon, + iconsIconNames, +} from '../ui/icons/.svg-sprite/index.js' + +export default function Page() { + return ( +
+ +

{iconsIconNames.length} icons available

+
+ ) +} +``` + +Webpack resolves the generated `new URL('../sprite.svg', import.meta.url)` and CSS Module, emitting a separate SVG asset. Keep the mode and the `--webpack` dev/build flags aligned. If custom Next.js webpack rules process SVG through SVGR, exclude `.svg-sprite/sprite.svg` from those rules. + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Viewer is interactive, so place the React bridge in a separate Client Component: + +```tsx +// src/app/icon-debug/IconsViewer.tsx +'use client' + +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../../ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export function IconsViewer() { + return +} +``` + +Render it from the route's Server Component: + +```tsx +// src/app/icon-debug/page.tsx +import { IconsViewer } from './IconsViewer' + +export default function IconDebugPage() { + return +} +``` + +Keep the route internal or development-only. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/webpack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'next@app/webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@app/webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/next-pages-turbopack.md b/docs/en/guides/next-pages-turbopack.md new file mode 100644 index 0000000..a1e53d3 --- /dev/null +++ b/docs/en/guides/next-pages-turbopack.md @@ -0,0 +1,140 @@ +# Next.js Pages Router Turbopack SVG Sprite Quick Start + +This guide targets the exact mode key `next@pages/turbopack`: a generated typed React icon for the Next.js Pages Router and Turbopack. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config and source icons together: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'next@pages/turbopack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Generate once per invocation and keep the exact Turbopack flags on both commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --turbopack", + "build": "npm run sprites && next build --turbopack" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +Import the generated component and icon-name list into a Pages Router page: + +```tsx +// src/pages/index.tsx +import { + IconsIcon, + iconsIconNames, +} from '../ui/icons/.svg-sprite/index.js' + +export default function HomePage() { + return ( +
+ +

{iconsIconNames.length} icons available

+
+ ) +} +``` + +The component works with SSR, SSG, and client-side navigation. Turbopack resolves the generated SVG URL and CSS Module and emits a separate asset. Keep the mode and the `--turbopack` dev/build flags aligned. + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Pages Router does not require an App Router Client Component boundary. Use the React bridge directly in a page with a static loader array: + +```tsx +// src/pages/icon-debug.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export default function IconDebugPage() { + return +} +``` + +Keep the page internal or development-only. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/turbopack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'next@pages/turbopack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@pages/turbopack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/next-pages-webpack.md b/docs/en/guides/next-pages-webpack.md new file mode 100644 index 0000000..32a97b2 --- /dev/null +++ b/docs/en/guides/next-pages-webpack.md @@ -0,0 +1,140 @@ +# Next.js Pages Router Webpack SVG Sprite Quick Start + +This guide targets the exact mode key `next@pages/webpack`: a generated typed React icon for the Next.js Pages Router and Webpack 5. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config adjacent to its source icons: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'next@pages/webpack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Generate once per invocation and keep the exact Webpack flags on both commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --webpack", + "build": "npm run sprites && next build --webpack" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +Import the generated component and icon-name list into a Pages Router page: + +```tsx +// src/pages/index.tsx +import { + IconsIcon, + iconsIconNames, +} from '../ui/icons/.svg-sprite/index.js' + +export default function HomePage() { + return ( +
+ +

{iconsIconNames.length} icons available

+
+ ) +} +``` + +The component works with SSR, SSG, and client-side navigation. Webpack resolves the generated SVG URL and CSS Module and emits a separate asset. Keep the mode and the `--webpack` dev/build flags aligned. If custom Next.js webpack rules process SVG through SVGR, exclude `.svg-sprite/sprite.svg` from those rules. + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Pages Router does not require an App Router Client Component boundary. Use the React bridge directly in a page with a static loader array: + +```tsx +// src/pages/icon-debug.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export default function IconDebugPage() { + return +} +``` + +Keep the page internal or development-only. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/webpack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'next@pages/webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@pages/webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/react-vite.md b/docs/en/guides/react-vite.md new file mode 100644 index 0000000..96ed9ea --- /dev/null +++ b/docs/en/guides/react-vite.md @@ -0,0 +1,148 @@ +# React Vite SVG Sprite Quick Start + +This guide targets the exact mode key `react@vite`: a generated typed React component with a Vite-managed SVG asset. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config and source icons together: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'react@vite', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Use the exact Vite dev/build commands and generate once per invocation: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && vite", + "build": "npm run sprites && tsc --noEmit && vite build" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +Import the generated component and icon-name list directly: + +```tsx +// src/App.tsx +import { + IconsIcon, + iconsIconNames, +} from './ui/icons/.svg-sprite/index.js' + +export function App() { + return ( +
+ + {iconsIconNames.length} icons available +
+ ) +} +``` + +`icon` is a generated union of SVG file names. Vite automatically handles the generated CSS Module and the `sprite.svg?no-inline` import, emitting the sprite as a separate asset. If your own TypeScript source imports Vite query assets, include Vite's ambient types: + +```json +{ + "compilerOptions": { + "types": ["vite/client"] + } +} +``` + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Use the React `SpriteViewer` bridge with a static loader array. Keep every `import()` path a string literal: + +```tsx +// src/IconsDebugPage.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('./ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export function IconsDebugPage() { + return +} +``` + +Keep this component on a debug route or in an internal tool. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'react@vite' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'react@vite', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/react-webpack.md b/docs/en/guides/react-webpack.md new file mode 100644 index 0000000..31a83fd --- /dev/null +++ b/docs/en/guides/react-webpack.md @@ -0,0 +1,156 @@ +# React Webpack SVG Sprite Quick Start + +This guide targets the exact mode key `react@webpack`: a generated typed React component using Webpack 5 Asset Modules and CSS Modules. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config adjacent to its source icons: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'react@webpack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Use the exact Webpack 5 flags and generate once per invocation: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && webpack serve --mode development", + "build": "npm run sprites && webpack --mode production" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +Import the generated component and icon-name list directly: + +```tsx +// src/App.tsx +import { + IconsIcon, + iconsIconNames, +} from './ui/icons/.svg-sprite/index.js' + +export function App() { + return ( +
+ + {iconsIconNames.length} icons available +
+ ) +} +``` + +The generated component uses `new URL('../sprite.svg', import.meta.url)`, which Webpack 5 processes through Asset Modules and emits as a separate SVG asset. Exclude `.svg-sprite/sprite.svg` from SVG component or SVGR rules so they do not intercept that URL dependency. + +The generated component also imports `react-component.module.css`. Configure `.module.css` through `css-loader` with modules enabled, plus `style-loader` or `MiniCssExtractPlugin`: + +```js +// webpack.config.js (relevant rule) +export default { + module: { + rules: [ + { + test: /\.module\.css$/i, + use: ['style-loader', { loader: 'css-loader', options: { modules: true } }], + }, + ], + }, +} +``` + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Use the React `SpriteViewer` bridge with a static loader array. Keep every `import()` path a string literal so Webpack can create the chunk: + +```tsx +// src/IconsDebugPage.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('./ui/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export function IconsDebugPage() { + return +} +``` + +Keep this component on a debug route or in an internal tool. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@webpack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'react@webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'react@webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/standalone-vite.md b/docs/en/guides/standalone-vite.md new file mode 100644 index 0000000..27f151e --- /dev/null +++ b/docs/en/guides/standalone-vite.md @@ -0,0 +1,156 @@ +# Standalone Vite SVG Sprite Quick Start + +This guide targets the exact mode key `standalone@vite`: a native generated Web Component with Vite-managed SVG assets. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config and source icons together: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'standalone@vite', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate once directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Use the exact Vite dev/build commands and generate once per invocation: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && vite", + "build": "npm run sprites && vite build" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. The generated JavaScript and declarations live together, and the declarations are self-contained: they do not require `@gromlab/svg-sprites`. + +### Production usage + +Register the generated element once, then use ``: + +```ts +// src/main.ts +import { + defineIconsIconElement, + iconsIconNames, +} from './ui/icons/.svg-sprite/index.js' + +defineIconsIconElement() +console.log('Available icons:', iconsIconNames) +``` + +```html + +``` + +The host is `1em` by `1em`, so `font-size` controls its default size. Transformed colors use `currentColor` and custom properties such as `--icon-color-1`: + +```css +icons-icon { + font-size: 24px; + color: #2563eb; + --icon-color-2: #dbeafe; +} +``` + +Vite handles the generated `sprite.svg?no-inline` import automatically and emits a separate asset. If your own TypeScript source imports Vite query assets, include Vite's ambient types: + +```json +{ + "compilerOptions": { + "types": ["vite/client"] + } +} +``` + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Register the Viewer element, import its type, and assign the generated JavaScript manifest to `sources`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './ui/icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('#debug')!.innerHTML = ` + +` + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +Keep this code on a debug route or in an internal tool. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'standalone@vite', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'standalone@vite' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'standalone@vite', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/standalone-webpack.md b/docs/en/guides/standalone-webpack.md new file mode 100644 index 0000000..29e477b --- /dev/null +++ b/docs/en/guides/standalone-webpack.md @@ -0,0 +1,143 @@ +# Standalone Webpack SVG Sprite Quick Start + +This guide targets the exact mode key `standalone@webpack`: a native generated Web Component using Webpack 5 Asset Modules. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`. + +Keep the config adjacent to its source icons: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +Use a plain default object export with no package import: + +```ts +// src/ui/icons/svg-sprite.config.ts +export default { + mode: 'standalone@webpack', + name: 'icons', +} +``` + +When `input` is omitted, SVG files are read from `./icons` relative to the config. A `.js` config with a default export and a `.json` config are also supported. Generate directly with: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts +``` + +Use the exact Webpack 5 flags and generate once per invocation: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.ts", + "dev": "npm run sprites && webpack serve --mode development", + "build": "npm run sprites && webpack --mode production" + } +} +``` + +Do not add `predev` or `prebuild` hooks to these scripts; that would run generation twice. In CI, replace `latest` with an exact package version. + +Generation creates a local `.gitignore`; commit that file once, but do not commit `.svg-sprite/`. Generated declarations are self-contained and do not require the package. + +### Production usage + +Register the generated element once, then use ``: + +```ts +// src/main.ts +import { + defineIconsIconElement, + iconsIconNames, +} from './ui/icons/.svg-sprite/index.js' + +defineIconsIconElement() +console.log('Available icons:', iconsIconNames) +``` + +```html + +``` + +The generated facade uses `new URL('./sprite.svg', import.meta.url)`, which Webpack 5 processes through Asset Modules and emits as a separate asset. If the project has SVG-to-component or SVGR rules, exclude `.svg-sprite/sprite.svg` from them so they do not intercept this URL dependency. Check `output.publicPath` if the emitted URL is wrong. + +## 2. Debug and preview + +This section is optional. Only users who need the Viewer or icon previews should install: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Register the Viewer element, import its type, and assign the generated JavaScript manifest to `sources`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './ui/icons/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('#debug')!.innerHTML = ` + +` + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +Keep this code on a debug route or in an internal tool. Viewer is not part of the production icon runtime. + +## 3. Type the config + +Choose one of these two paths. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'standalone@webpack', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Copy a mode-specific type directly into the config: + +```ts +type LocalSpriteConfig = { + mode: 'standalone@webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'standalone@webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/guides/standalone.md b/docs/en/guides/standalone.md new file mode 100644 index 0000000..4309921 --- /dev/null +++ b/docs/en/guides/standalone.md @@ -0,0 +1,171 @@ +# Standalone SVG Sprite Quick Start + +This guide targets the exact mode key `standalone`: static HTML or a custom asset pipeline with no generated JavaScript facade. + +## 1. Generate the sprite + +No package installation and no `package.json` dependency are needed. `npx` downloads the CLI temporarily, and generated runtime does not import `@gromlab/svg-sprites`; bare `standalone` does not generate a JavaScript runtime at all. + +Keep the config next to its source icons: + +```text +src/ui/icons/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.json +``` + +Create a JSON config: + +```json +{ + "mode": "standalone", + "name": "icons" +} +``` + +When `input` is omitted, the generator reads `./icons` next to the config. + +To include SVG files from nested directories, set one glob pattern: + +```json +{ + "mode": "standalone", + "name": "icons", + "input": "../assets/icons/**/*.svg" +} +``` + +An array can mix folders, individual SVG files, and glob patterns: + +```json +{ + "mode": "standalone", + "name": "icons", + "input": [ + "../assets/icons", + "../features/profile/user.svg", + "../features/admin/*.svg" + ] +} +``` + +All relative paths start at the config directory. Pass the config path explicitly: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.json +``` + +For repeatable local commands, the same temporary CLI can be used from a script: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/icons/svg-sprite.config.json" + } +} +``` + +Run that script once from your existing dev/build pipeline. If you use a `predev` or `prebuild` hook instead, do not also call `npm run sprites` inside the corresponding script. Bare `standalone` has no bundler-specific dev or build flag. In CI, replace `latest` with an exact package version. + +Bare `standalone` does not create or modify `.gitignore`; the application decides whether `.svg-sprite/` is committed or ignored. It emits no declarations, facade, or component. Generated declarations in typed modes are self-contained and do not require the package. + +### Production usage + +The application owns the public URL, versioning, and cache policy. Copy both generated assets into the deploy output: + +```bash +cp src/ui/icons/.svg-sprite/sprite.svg public/assets/icons.svg +cp src/ui/icons/.svg-sprite/svg-sprite.manifest.json public/assets/icons.manifest.json +``` + +Use the published URL manually: + +```html + + + +``` + +Safe icon names normally match their fragment IDs. Names containing spaces or other SVG-ID-unsafe characters receive a generated ID; read that icon's `id` from `icons.manifest.json` instead of constructing the fragment yourself. + +## 2. Debug and preview + +This section is optional. Only install the package locally if you need the debug Viewer or an icon preview: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Without a bundler, self-host the browser bundle by copying it from `node_modules` into the deploy output: + +```bash +cp node_modules/@gromlab/svg-sprites/dist/viewer-element.js public/debug/viewer-element.js +``` + +Then provide both public asset URLs through HTML attributes: + +```html + + + +``` + +For a quick preview, a pinned CDN file can replace the self-hosted script: + +```html + +``` + +Self-host the file for controlled environments and pin the CDN version if you use the alternative. Viewer is optional tooling, not part of the production icon runtime. + +## 3. Type the config + +This guide uses a JSON config, which works without TypeScript types. If config autocomplete is needed, replace `svg-sprite.config.json` with `svg-sprite.config.ts` and choose one of these approaches. + +### With a local package installation + +After installing the package locally, use the helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'standalone', + name: 'icons', +}) +``` + +You can alternatively import `type SpriteConfig` and apply `satisfies SpriteConfig`. + +### Without the package + +Keep a narrow local type directly in the config: + +```ts +type LocalSpriteConfig = { + mode: 'standalone' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'standalone', + name: 'icons', +} satisfies LocalSpriteConfig +``` diff --git a/docs/en/next-app.md b/docs/en/next-app.md index 1118741..97c95a7 100644 --- a/docs/en/next-app.md +++ b/docs/en/next-app.md @@ -1,113 +1,4 @@ -# Next.js App Router +# Next.js App Router Guides Moved -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | -|---|---| -| Turbopack | `next@app/turbopack` | -| Webpack 5 | `next@app/webpack` | - -## 1. Install the package - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'file-manager', - description: 'File manager icons', -}) -``` - -The root barrel is application-owned: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -## 3. Add generation - -For Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Webpack, replace the mode key with `next@app/webpack`. - -Run the first generation before importing the generated module: - -```bash -npm run sprite:file-manager -``` - -## 4. Use it in a Server Component - -The generated component does not contain `'use client'`, so it can be imported directly into `page.tsx` or `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js emits a separate SVG asset with a content hash. The same generated code is used during SSR and in the browser, with no URL mismatch. - -## 5. Add SpriteViewer - -The viewer is interactive, so it requires a separate Client Component boundary: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -Run the project's build script configured for the selected bundler: - -```bash -npm run build -``` - -The build script and the generator mode key must target the same bundler. +- [App Router + Turbopack](guides/next-app-turbopack.md) +- [App Router + Webpack](guides/next-app-webpack.md) diff --git a/docs/en/next-pages.md b/docs/en/next-pages.md index 20cfc11..c4763b9 100644 --- a/docs/en/next-pages.md +++ b/docs/en/next-pages.md @@ -1,105 +1,4 @@ -# Next.js Pages Router +# Next.js Pages Router Guides Moved -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | -|---|---| -| Turbopack | `next@pages/turbopack` | -| Webpack 5 | `next@pages/webpack` | - -## 1. Install the package - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/webpack', - name: 'file-manager', - description: 'File manager icons', -}) -``` - -The root barrel is application-owned: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -## 3. Add generation - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Turbopack, replace the mode key with `next@pages/turbopack`. - -Run the first generation before importing the generated module: - -```bash -npm run sprite:file-manager -``` - -## 4. Use it on a page - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -The component works the same way with SSR, SSG, and client-side navigation. Next.js emits a separate SVG asset with a content hash. - -## 5. Add SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -Run the project's build script configured for the selected bundler: - -```bash -npm run build -``` - -The build script and the generator mode key must target the same bundler. +- [Pages Router + Turbopack](guides/next-pages-turbopack.md) +- [Pages Router + Webpack](guides/next-pages-webpack.md) diff --git a/docs/en/programmatic-api.md b/docs/en/programmatic-api.md index d5c42a0..ad49c97 100644 --- a/docs/en/programmatic-api.md +++ b/docs/en/programmatic-api.md @@ -1,147 +1,3 @@ -# Programmatic API +# Programmatic API Moved -[← Back to home](../../README.md) - -The package is ESM-only and provides one Node.js generation API. The framework-neutral Viewer is available from `@gromlab/svg-sprites/viewer`, its auto-register entry from `@gromlab/svg-sprites/viewer/element`, and the React bridge from `@gromlab/svg-sprites/react`. - -## `generateSprite` - -```ts -import { generateSprite } from '@gromlab/svg-sprites' - -const result = await generateSprite( - 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', -) -``` - -For static standalone mode, use `result.spritePath` in a build script to publish the -SVG under an application URL: - -```ts -import { copyFile } from 'node:fs/promises' - -const result = await generateSprite('src/sprite/svg-sprite.config.ts', { - mode: 'standalone', -}) -await copyFile(result.spritePath, 'dist/app-icons/sprite.svg') -``` - -`spritePath` is a filesystem path, not a browser URL. A deployment-neutral JSON -manifest is available through `result.manifestPath` and is copied independently. - -The first argument accepts the full path to an explicitly selected `.ts`, `.js`, or `.json` config file with any name. Passing a directory enables config-less mode and uses that directory as the sprite module root. - -The second argument contains optional overrides and always takes precedence over the config: - -```ts -await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { - mode: 'react@webpack', - name: 'documents', - inputFolder: './assets', - inputFiles: ['../../shared/search.svg'], - transform: { - addTransition: false, - }, - generatedNotice: false, -}) -``` - -Configuration is resolved in this order: - -```text -defaults → config → API overrides -``` - -For fully programmatic generation, pass a directory and provide the required settings as overrides: - -```ts -await generateSprite('src/ui/file-manager/svg-sprite', { - mode: 'react@vite', - name: 'file-manager', - inputFiles: [ - '../../shared/search.svg', - '../../shared/settings.svg', - ], -}) -``` - -## Configuration - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../shared/check.svg'], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`defineSpriteConfig` is an identity helper for TypeScript autocomplete. JavaScript can export the same object with `export default`, while JSON contains the object directly. - -## Specialized wrappers - -The specialized functions are available as wrappers around `generateSprite`: - -```ts -import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' - -await generateReactSprite('path/to/config.ts', 'vite') -await generateNextSprite('path/to/config.ts', { - router: 'app', - bundler: 'turbopack', -}) -``` - -An explicitly supplied target overrides `mode` from the file. Prefer `generateSprite` in new code. - -## Config API - -```ts -import { - loadSpriteConfig, - resolveSpriteConfig, - validateSpriteConfig, -} from '@gromlab/svg-sprites' -``` - -- `loadSpriteConfig(file)` loads an explicitly selected `.ts`, `.js`, or `.json` file. -- `validateSpriteConfig(value)` performs runtime validation. -- `resolveSpriteConfig(root, config, overrides)` merges values, applies defaults, and resolves paths relative to `root`. - -## Low-level compiler - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, -} from '@gromlab/svg-sprites' -``` - -These functions are intended for custom orchestration. Standard generation should use `generateSprite`. - -## Viewer runtime - -```ts -import '@gromlab/svg-sprites/viewer/element' -import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -``` - -The browser entry registers ``. Bare standalone can also load the self-contained `dist/viewer-element.js` without a bundler. - -The React bridge keeps the component API: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -`SpriteViewer` accepts generated manifests, remote standalone sources, lazy loaders, or an `import.meta.glob` result. The React entry contains `'use client'` and is intended for debug tools; production components are imported from local sprite modules. +The canonical document is now the [programmatic API](reference/programmatic-api.md). diff --git a/docs/en/react-vite.md b/docs/en/react-vite.md index 4f882a5..ed23a3c 100644 --- a/docs/en/react-vite.md +++ b/docs/en/react-vite.md @@ -1,125 +1,3 @@ -# React + Vite +# React + Vite Guide Moved -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Vite project. - -The result is a typed React component and a separate cacheable SVG asset. - -## 1. Install the package - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'File manager icons', -}) -``` - -The root barrel is application-owned and explicitly re-exports the generated API: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Unified configuration](reference.md#unified-configuration). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -The name `file-manager` is converted to `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, rendering methods, and color controls are described in the [technical reference](reference.md#react-component-and-typescript). - -Vite emits the sprite as a separate file named like `assets/sprite-.svg`. SVG path data is not included in JavaScript. - -## 6. Add a debug page - -After integrating the icons, you can display all React sprites with `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite automatically finds the generated manifest for each React sprite. The `import.meta.glob` pattern must be a string literal, and generation must run before Vite starts. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `.svg-sprite/index.js`: run `npm run sprite:file-manager`. -- The Viewer cannot find the sprite: check the glob path to `.svg-sprite/svg-sprite.manifest.js`. -- `Refusing to overwrite a user file` error: there is a user file at a generated path. -- The icon does not change color: use `color` or `--icon-color-N`. +The canonical guide is now [React + Vite](guides/react-vite.md). diff --git a/docs/en/react-webpack.md b/docs/en/react-webpack.md index 1d1926c..dbdd36c 100644 --- a/docs/en/react-webpack.md +++ b/docs/en/react-webpack.md @@ -1,129 +1,3 @@ -# React + Webpack 5 +# React + Webpack Guide Moved -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Webpack 5 project. - -The result is a typed React component and a separate SVG asset emitted through Webpack Asset Modules. - -## 1. Install the package - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@webpack', - name: 'file-manager', - description: 'File manager icons', -}) -``` - -The root barrel is application-owned and explicitly re-exports the generated API: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Unified configuration](reference.md#unified-configuration). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, rendering methods, and color controls are described in the [technical reference](reference.md#react-component-and-typescript). - -Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset. - -If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules. - -The generated component imports `react/react-component.module.css`, so Webpack must process CSS Modules through `css-loader` and `style-loader` or `MiniCssExtractPlugin`. If the TypeScript project does not include a declaration for CSS Modules, add one separately. - -## 6. Add a debug page - -Webpack does not support Vite's `import.meta.glob` API, so provide static loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), - () => import('./ui/navigation/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export const IconsDebugPage = () => ( - -) -``` - -The paths in `import()` must be string literals. Webpack creates chunks for the manifests and associates them with the SVG assets. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `.svg-sprite/index.js`: run `npm run sprite:file-manager`. -- The Viewer does not load the sprite: check the `import()` path to `.svg-sprite/svg-sprite.manifest.js`. -- Incorrect asset URL: check `output.publicPath`. -- Another loader intercepts the SVG: exclude the generated sprite from the incompatible rule. - -For Next.js, use the separate mode keys described in the [App Router](next-app.md) and [Pages Router](next-pages.md) guides. +The canonical guide is now [React + Webpack](guides/react-webpack.md). diff --git a/docs/en/reference.md b/docs/en/reference.md index f132563..8138c45 100644 --- a/docs/en/reference.md +++ b/docs/en/reference.md @@ -1,555 +1,3 @@ -# Technical reference +# Technical Reference Moved -[← Back to home](../../README.md) - -Reference for the configuration, generated API, and behavior of `@gromlab/svg-sprites`. For step-by-step setup instructions, see the guide for your stack: - -- [Next.js App Router](next-app.md) -- [Next.js Pages Router](next-pages.md) -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) - -## Requirements - -- Node.js 18 or newer; -- the package is distributed as ESM and is loaded with `import`; -- React 18 or 19 is required for generated components and `@gromlab/svg-sprites/react`; -- for typed package exports, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## CLI and generation modes - -The CLI accepts exactly one path: an explicitly selected config file or a directory for config-less generation: - -```text -svg-sprites [options] -``` - -| Environment | Mode | -|---|---| -| Static HTML / custom publishing | `standalone` | -| Standalone + Vite | `standalone@vite` | -| Standalone + Webpack 5 | `standalone@webpack` | -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | - -The config file may have any name and use the `.ts`, `.js`, or `.json` extension. The CLI does not discover it by convention: pass the file explicitly. The guides use `svg-sprite.config.ts` as the recommended name. - -When a directory is passed, all settings come from CLI options. When a config file is passed, CLI options override the file. The full order is `defaults → config → CLI`. - -Available options are `--mode`, `--name`, `--description`, `--input-folder`, repeatable `--input-file`, plus the `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition`, and `--generated-notice`/`--no-generated-notice` pairs. Transform flags override individual fields, while supplying at least one `--input-file` replaces the complete config `inputFiles` array. - -The mode must match the application's publishing strategy. Bare `standalone` leaves the public URL to the application; Vite and Webpack modes generate bundler-specific SVG asset integration. - -## Unified configuration - -Each config file defines one independent sprite. - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'app', - description: 'Shared application icons', - inputFolder: './local-icons', - inputFiles: [ - '../../assets/icons/search.svg', - '../../assets/icons/settings.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Option | Type | Default | Purpose | -|---|---|---|---| -| `mode` | `SpriteMode` | None | Generation mode; may be supplied by CLI/API | -| `name` | `string` | Derived from the directory | Name of the sprite, component, and public types | -| `description` | `string` | None | Description for types and the debug manifest | -| `inputFolder` | `string` | `./icons` | SVG directory relative to the module root | -| `inputFiles` | `string[]` | `[]` | Paths to individual SVG files relative to the module root | -| `transform` | `TransformOptions` | All enabled | SVG preparation settings | -| `generatedNotice` | `boolean` | `true` | Full or abbreviated warning in generated files | - -### Sprite name - -`name` is written in kebab-case and must start with an ASCII letter: - -```text -app → AppIcon -file-manager → FileManagerIcon -``` - -If `name` is omitted, the generator derives it from the directory. For a directory named `svg-sprite` or `svg-sprites`, the parent directory's name is used. - -### Icon sources - -`inputFolder` and `inputFiles` are combined into one set. This lets you keep local SVG files next to a module and add shared icons from other parts of the project without copying them. - -If `inputFiles` is populated and the implicit `./icons` directory does not exist, generation uses only the file list. An explicitly configured `inputFolder` that does not exist is an error. - -Only the top level of the directory is scanned. Nested directories are not traversed recursively. For a nested structure, list the exact paths through `inputFiles`. - -Identical absolute paths are deduplicated. Different SVG files with the same file name are treated as a conflict because the public icon name is derived from the basename. - -## Generated module - -After generation, a React or Next.js sprite directory looks like this: - -```text -app-icons/ -├── .gitignore -├── svg-sprite.config.ts -├── index.ts # optional user-owned barrel -└── .svg-sprite/ - ├── state.json - ├── index.js - ├── index.d.ts - ├── icon-data.js - ├── icon-data.d.ts - ├── sprite.svg - ├── svg-sprite.manifest.js - ├── svg-sprite.manifest.d.ts - └── react/ - ├── react-component.js - ├── react-component.d.ts - └── react-component.module.css -``` - -| File | Purpose | -|---|---| -| `.svg-sprite/index.js` | Production exports for the component and runtime icon-name list | -| `.svg-sprite/index.d.ts` | Public declarations for the component, props, styles, and icon-name union | -| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata and the asset URL for `SpriteViewer` | -| `.svg-sprite/sprite.svg` | Compiled SVG sprite | -| `.svg-sprite/react/react-component.js` | React component runtime without TypeScript or JSX | -| `.svg-sprite/react/react-component.d.ts` | React component props, style, and declaration | -| `.svg-sprite/react/react-component.module.css` | Styles for the React implementation | -| `.svg-sprite/icon-data.js` | Runtime icon-name list and internal IDs | -| `.svg-sprite/*.d.ts` | TypeScript declarations for the corresponding JavaScript modules | -| `.svg-sprite/state.json` | Mode, contract version, and managed file list | - -Standalone contracts do not create `react/`. Bare `standalone` contains only the -runtime asset and deployment-neutral manifest data: - -```text -.svg-sprite/ -├── state.json -├── sprite.svg -└── svg-sprite.manifest.json -``` - -`standalone@vite` and `standalone@webpack` additionally create `index.*`, -`icon-data.*`, and a resolved `svg-sprite.manifest.*`. - -The generator overwrites and deletes only files that contain its marker. If a user file occupies a managed path, generation fails. The root `index.ts` is user-owned; create a barrel when needed: - -```ts -export * from './.svg-sprite' -``` - -## React component and TypeScript - -A sprite with `name: 'app'` exports: - -```ts -export { AppIcon, appIconNames } -export type { AppIconName, AppIconProps, AppIconStyle } -``` - -### Icon names - -SVG file names become valid `icon` values: - -```tsx - - // TypeScript error -``` - -The runtime list contains the same values: - -```ts -import { appIconNames } from '@/ui/app-icons' - -// readonly ['search', 'settings', 'user'] -``` - -Names containing spaces or other characters that are unsafe in SVG IDs remain part of the public API. For the internal fragment ID, the generator creates a stable, safe hash: - -```text -folder open.svg → icon="folder open" → id="icon-" -``` - -For these names, use the generated component or the `id` from the debug manifest instead of constructing the fragment ID manually. - -### SVG attributes - -By default, the component renders an `` and accepts standard SVG attributes: - -```tsx - -``` - -The component does not add accessibility semantics automatically. Pass appropriate `aria-*` attributes, a `role`, or a label based on the icon's purpose. - -### Wrapper - -`wrapped` renders a `` containing the SVG. In this mode, the remaining props apply to the ``: - -```tsx - -``` - -### Typed CSS custom properties - -`AppIconStyle` extends `CSSProperties` and supports properties in the form `--icon-color-N`: - -```tsx - -``` - -## Multiple sprites - -Each directory with a configuration creates an independent component, types, manifest, and SVG asset: - -```text -app-icons → AppIcon → shared icons -analytics-icons → AnalyticsIcon → analytics page icons -editor-icons → EditorIcon → editor icons -``` - -The same source SVG can be added to multiple configurations through `inputFiles`. You do not need to copy the file into each sprite directory. - -For multiple sprites, add a separate CLI command for each directory or combine the commands in a shared npm script. - -## Formats and rendering methods - -All current modes generate the `stack` format. - -| Format | `` | `` | CSS background | -|---|---:|---:|---:| -| `stack` | Yes | Yes | Yes | - -### Generated component - -For React and Next.js, use the generated component. It knows the internal IDs, constructs the URL, and provides a TypeScript API: - -```tsx - -``` - -### Manually with `` - -How you obtain `spriteUrl` depends on the bundler. - -Static HTML after the application publishes `.svg-sprite/sprite.svg`: - -```html - -``` - -Standalone Vite/Webpack provides generated `getIconsIconHref()` and an internal ID -map. Do not construct fragments from unsafe file names manually. - -Vite: - -```ts -import spriteUrl from './.svg-sprite/sprite.svg?no-inline' -``` - -Webpack 5, Turbopack, and Next.js: - -```ts -const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href -``` - -After obtaining the URL, use it in JSX: - -```tsx - - - -``` - -For names that are unsafe as SVG IDs, use the internal `id` from the manifest. - -### With `` - -```tsx -Search -``` - -An SVG inside `` is isolated from the page's CSS. Setting `color` or `--icon-color-N` on the outer element does not change its internal colors. - -### With CSS - -```css -.icon { - background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; -} -``` - -For a single-color silhouette, you can use a mask: - -```css -.icon { - background-color: currentColor; - mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; -} -``` - -A mask does not preserve original colors, gradients, or differences between `fill` and `stroke`. - -The path in CSS is resolved relative to the CSS file itself. In these examples, the CSS file is next to `svg-sprite.config.ts`. - -## Assets and caching - -The generated component or standalone facade passes the SVG to the bundler as a separate asset: - -- Vite uses a static import with `?no-inline`; -- Webpack 5, Turbopack, and Next.js use `new URL(..., import.meta.url)`; -- SVG path data is not serialized into generated JavaScript. - -Bare `standalone` does not participate in an asset pipeline: the application copies -or publishes `sprite.svg` and owns its URL, versioning, and cache policy. - -With standard asset naming, the bundler adds a content hash: - -```text -/assets/sprite-.svg -``` - -This allows the SVG to be cached separately from JavaScript. Changing React code does not change the sprite contents, while changing icons creates a new asset version. - -HTTP cache headers, CDN behavior, and `Cache-Control` are configured by the application or hosting platform. With Webpack, the final file name depends on the project's `assetModuleFilename`. - -## SVG transformations - -All transformations are enabled by default and can be configured independently: - -| Option | Behavior | -|---|---| -| `removeSize` | Removes `width` and `height` from the root `` while preserving an existing `viewBox` | -| `replaceColors` | Replaces detected `fill` and `stroke` values with `--icon-color-N` | -| `addTransition` | Adds transitions for `fill` and `stroke` to colored elements and generated styles | - -To disable an individual operation: - -```ts -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - transform: { - removeSize: false, - replaceColors: false, - addTransition: false, - }, -}) -``` - -Source SVG files are not modified. Transformations apply only to the generated sprite contents. - -## Color management - -### Monochrome icons - -If one color is detected, its fallback becomes `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -Set the color through a prop or CSS: - -```tsx - -``` - -### Multicolor icons - -Each unique color gets its own custom property with the original color as its fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -You can override only the values you need: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Limitations - -- `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced; -- colors in `fill`, `stroke`, and inline `style` attributes are handled most reliably; -- CSS classes and external stylesheets inside the SVG are not the primary transformation use case; -- `url(#...)` values may be replaced along with colors, so gradients and patterns require a separate sprite with `replaceColors: false`; -- masks, filters, and complex internal CSS rules require visual verification; -- page CSS custom properties are available through ``, but not inside `` or a CSS background. - -For a complex icon, you can disable `replaceColors` in a separate sprite configuration. - -## SpriteViewer - -The Viewer uses one Shadow DOM Web Component for every mode. React and future framework components are bridges to that same element, so the visuals and behavior are not duplicated. - -Bare `standalone` loads the self-contained browser bundle and supplies the JSON manifest URL and the published SVG URL: - -```html - - - -``` - -`viewer-element.js` has no additional runtime files and can be copied with the other static assets for self-hosting. - -`standalone@vite` and `standalone@webpack` register the same element through an npm entry and pass the generated JS manifest through the `sources` property: - -```ts -import '@gromlab/svg-sprites/viewer/element' -import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -import spriteManifest from './svg-sprite/.svg-sprite/svg-sprite.manifest.js' - -const viewer = document.querySelector('gromlab-sprite-viewer')! -viewer.sources = [spriteManifest] -``` - -React and Next.js keep the component API: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -It accepts ready-made manifests, remote standalone sources, an array of lazy loaders, or a record in the format returned by `import.meta.glob`. - -Vite: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export const IconsDebugPage = () => ( - -) -``` - -Webpack and Next.js: - -```tsx -const sources = [ - () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), - () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), -] - -export const IconsDebugPage = () => ( - -) -``` - -The Viewer displays groups, search, `viewBox`, CSS custom properties, and fallback colors. React/Next manifests get React, SVG, IMG, and CSS tabs; standalone manifests get SVG, IMG, and CSS. You can change color values in the interface and immediately inspect the result. - -### Viewer theme - -By default, `colorTheme="auto"` follows `prefers-color-scheme`. You can explicitly pass `light` or `dark`: - -```tsx - -``` - -To synchronize it with the application theme: - -```tsx - -``` - -`@gromlab/svg-sprites/react` contains `'use client'` and renders the Web Component host; its internal Shadow DOM is created after the browser runtime loads. In the Next.js App Router, place the Viewer inside a separate Client Component boundary and use it only on a debug route or in an internal tool. - -## Generated files, Git, and CI - -A modern sprite module creates a local `.gitignore` for: - -```text -/.svg-sprite/ -``` - -Commit the local `.gitignore` to the repository once. It excludes the other generated files, so generation must run before commands that import the sprite module: - -```json -{ - "scripts": { - "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", - "predev": "npm run sprites", - "prebuild": "npm run sprites", - "pretypecheck": "npm run sprites" - } -} -``` - -CI must install development dependencies and run the generation script before building or type-checking. - -If the sprite directory already contains a user-created `.gitignore` or a user-owned file inside `.svg-sprite`, the generator will not overwrite it. The root `index.ts` remains user-owned and may re-export the generated API. - -## Troubleshooting - -- Missing `.svg-sprite/index.js`: run the generation script before importing the generated module. -- Source not found: pass an existing config file or sprite module directory. -- Mode missing: add `mode` to the config or pass `--mode`. -- Icon missing from the type: check `inputFiles`, the `.svg` extension, and the nesting level under `inputFolder`. -- Name conflict: two different SVG files have the same basename; rename one of them. -- `Refusing to overwrite a user file`: a file without the generated marker occupies a managed path. -- The icon does not change color: use `` or the generated component and check `replaceColors`. -- Webpack emits an incorrect URL: check Asset Modules, `output.publicPath`, and SVG loaders. -- Static sprite returns 404: check the post-generation copy or server alias, and do not put a filesystem `spritePath` into HTML. -- The Viewer cannot find the sprite: check the path to `.svg-sprite/svg-sprite.manifest.js` and run generation before starting the application. -- Build and mode do not match: use the target that corresponds to the actual bundler. - -For custom orchestration and low-level compilation, see the [Programmatic API](programmatic-api.md). +The canonical document is now the [technical reference](reference/technical.md). diff --git a/docs/en/reference/README.md b/docs/en/reference/README.md new file mode 100644 index 0000000..a3bafd6 --- /dev/null +++ b/docs/en/reference/README.md @@ -0,0 +1,4 @@ +# Reference + +- [Technical reference](technical.md) +- [Programmatic API](programmatic-api.md) diff --git a/docs/en/reference/programmatic-api.md b/docs/en/reference/programmatic-api.md new file mode 100644 index 0000000..cef0c34 --- /dev/null +++ b/docs/en/reference/programmatic-api.md @@ -0,0 +1,147 @@ +# Programmatic API + +[Documentation index](../README.md) + +The package is ESM-only and provides one Node.js generation API. The framework-neutral Viewer is available from `@gromlab/svg-sprites/viewer`, its auto-register entry from `@gromlab/svg-sprites/viewer/element`, and the React bridge from `@gromlab/svg-sprites/react`. + +## `generateSprite` + +```ts +import { generateSprite } from '@gromlab/svg-sprites' + +const result = await generateSprite( + 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', +) +``` + +For static standalone mode, use `result.spritePath` in a build script to publish the +SVG under an application URL: + +```ts +import { copyFile } from 'node:fs/promises' + +const result = await generateSprite('src/sprite/svg-sprite.config.ts', { + mode: 'standalone', +}) +await copyFile(result.spritePath, 'dist/app-icons/sprite.svg') +``` + +`spritePath` is a filesystem path, not a browser URL. A deployment-neutral JSON +manifest is available through `result.manifestPath` and is copied independently. + +The first argument accepts the full path to an explicitly selected `.ts`, `.js`, or `.json` config file with any name. Passing a directory enables config-less mode and uses that directory as the sprite module root. + +The second argument contains optional overrides and always takes precedence over the config: + +```ts +await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { + mode: 'react@webpack', + name: 'documents', + input: ['./assets', '../../shared/search.svg'], + transform: { + addTransition: false, + }, + generatedNotice: false, +}) +``` + +Configuration is resolved in this order: + +```text +defaults → config → API overrides +``` + +For fully programmatic generation, pass a directory and provide the required settings as overrides: + +```ts +await generateSprite('src/ui/file-manager/svg-sprite', { + mode: 'react@vite', + name: 'file-manager', + input: [ + '../../shared/search.svg', + '../../shared/settings.svg', + ], +}) +``` + +## Configuration + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'file-manager', + description: 'File manager icons', + input: ['./icons', '../../shared/check.svg'], + transform: { + removeSize: true, + replaceColors: true, + addTransition: true, + }, + generatedNotice: true, +}) +``` + +`input` accepts one folder, SVG file, or glob pattern, or an array that combines them. When omitted, it defaults to `./icons`; relative paths start at the config directory. + +`defineSpriteConfig` is an identity helper for TypeScript autocomplete. JavaScript can export the same object with `export default`, while JSON contains the object directly. + +## Specialized wrappers + +The specialized functions are available as wrappers around `generateSprite`: + +```ts +import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' + +await generateReactSprite('path/to/config.ts', 'vite') +await generateNextSprite('path/to/config.ts', { + router: 'app', + bundler: 'turbopack', +}) +``` + +An explicitly supplied target overrides `mode` from the file. Prefer `generateSprite` in new code. + +## Config API + +```ts +import { + loadSpriteConfig, + resolveSpriteConfig, + validateSpriteConfig, +} from '@gromlab/svg-sprites' +``` + +- `loadSpriteConfig(file)` loads an explicitly selected `.ts`, `.js`, or `.json` file. +- `validateSpriteConfig(value)` performs runtime validation. +- `resolveSpriteConfig(root, config, overrides)` merges values, applies defaults, and resolves paths relative to `root`. + +## Low-level compiler + +```ts +import { + compileSprite, + compileSpriteContent, + createShapeTransform, +} from '@gromlab/svg-sprites' +``` + +These functions are intended for custom orchestration. Standard generation should use `generateSprite`. + +## Viewer runtime + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +``` + +The browser entry registers ``. Bare standalone can also load the self-contained `dist/viewer-element.js` without a bundler. + +The React bridge keeps the component API: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +``` + +`SpriteViewer` accepts generated manifests, remote standalone sources, lazy loaders, or an `import.meta.glob` result. The React entry contains `'use client'` and is intended for debug tools; production components are imported from local sprite modules. diff --git a/docs/en/reference/technical.md b/docs/en/reference/technical.md new file mode 100644 index 0000000..d89a8ce --- /dev/null +++ b/docs/en/reference/technical.md @@ -0,0 +1,632 @@ +# Technical reference + +[Documentation index](../README.md) + +Reference for the configuration, generated API, and behavior of `@gromlab/svg-sprites`. For step-by-step setup instructions, see the guide for your stack: + +- [Bare standalone](../guides/standalone.md) +- [Standalone + Vite](../guides/standalone-vite.md) +- [Standalone + Webpack 5](../guides/standalone-webpack.md) +- [React + Vite](../guides/react-vite.md) +- [React + Webpack 5](../guides/react-webpack.md) +- [Next.js App Router + Turbopack](../guides/next-app-turbopack.md) +- [Next.js App Router + Webpack](../guides/next-app-webpack.md) +- [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md) +- [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md) + +## Requirements + +- Node.js 18 or newer; +- the package is distributed as ESM and is loaded with `import`; +- React 18 or 19 is required only for React/Next generated components and `@gromlab/svg-sprites/react`; +- for typed package exports, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. + +Generation does not require a project dependency. Run the CLI through `npx`: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites path/to/svg-sprite.config.ts +``` + +Install the package as a development dependency only when the project needs the +Viewer, config types, or the programmatic API: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +## CLI and generation modes + +The CLI accepts exactly one path: an explicitly selected config file or a directory for config-less generation: + +```text +svg-sprites [options] +``` + +| Environment | Mode | +|---|---| +| Static HTML / custom publishing | `standalone` | +| Standalone + Vite | `standalone@vite` | +| Standalone + Webpack 5 | `standalone@webpack` | +| React + Vite | `react@vite` | +| React + Webpack 5 | `react@webpack` | +| Next.js App Router + Turbopack | `next@app/turbopack` | +| Next.js App Router + Webpack 5 | `next@app/webpack` | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | +| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | + +The config file may have any name and use the `.ts`, `.js`, or `.json` extension. The CLI does not discover it by convention: pass the file explicitly. The guides use `svg-sprite.config.ts` as the recommended name. + +When a directory is passed, all settings come from CLI options. When a config file is passed, CLI options override the file. The full order is `defaults → config → CLI`. + +Available options are `--mode`, `--name`, `--description`, repeatable `--input `, plus the `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition`, and `--generated-notice`/`--no-generated-notice` pairs. Transform flags override individual fields, while supplying at least one `--input` replaces the complete config `input` value. + +Quote CLI glob patterns with single quotes so the shell does not expand them before the generator receives them: + +```bash +svg-sprites --input './icons/**/*.svg' --input '!./icons/legacy/**' svg-sprite.config.ts +``` + +The mode must match the application's publishing strategy. Bare `standalone` leaves the public URL to the application; Vite and Webpack modes generate bundler-specific SVG asset integration. + +## Unified configuration + +Each config file defines one independent sprite. + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + name: 'app', + description: 'Shared application icons', + input: [ + './local-icons', + '../../assets/icons/*.svg', + '!../../assets/icons/deprecated-*.svg', + ], + transform: { + removeSize: true, + replaceColors: true, + addTransition: true, + }, + generatedNotice: true, +}) +``` + +| Option | Type | Default | Purpose | +|---|---|---|---| +| `mode` | `SpriteMode` | None | Generation mode; may be supplied by CLI/API | +| `name` | `string` | Derived from the directory | Name of the sprite, component, and public types | +| `description` | `string` | None | Description for types and the debug manifest | +| `input` | `string \| string[]` | `./icons` | SVG folders, files, and glob patterns relative to the config directory | +| `transform` | `TransformOptions` | All enabled | SVG preparation settings | +| `generatedNotice` | `boolean` | `true` | Full or abbreviated warning in generated files | + +### Sprite name + +`name` is written in kebab-case and must start with an ASCII letter: + +```text +app → AppIcon +file-manager → FileManagerIcon +``` + +If `name` is omitted, the generator derives it from the directory. For a directory named `svg-sprite` or `svg-sprites`, the parent directory's name is used. + +### Icon sources + +`SpriteConfig.input` is optional and has the type `string | string[]`. When it is omitted, the source is the literal `./icons` folder relative to the config directory. In config-less mode, relative paths start at the directory passed to the CLI or API. + +Each positive string may be a literal folder, a literal `.svg` file, or a glob pattern. A literal folder includes only its immediate `*.svg` children. Use an explicit pattern such as `icons/**/*.svg` to traverse nested directories. + +An array combines all positive sources. A pattern prefixed with `!` excludes its matches from the combined result globally, regardless of which positive source included them. + +Supported glob syntax includes: + +| Syntax | Meaning | +|---|---| +| `*` | Any characters within one path segment | +| `**` | Any number of nested directories | +| `?` | One character within a path segment | +| `{a,b}` | Either alternative | +| `[abc]` | One character from the set or range | +| `!pattern` | Exclude matches from the full combined input | + +Every positive source or pattern must find at least one SVG, otherwise generation fails. Duplicate paths are removed and the final file list is sorted deterministically. Different SVG files with the same basename remain a conflict because the basename defines the public icon name. + +## Generated module + +After generation, a React or Next.js sprite directory looks like this: + +```text +app-icons/ +├── .gitignore +├── svg-sprite.config.ts +├── index.ts # optional user-owned barrel +└── .svg-sprite/ + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts + ├── sprite.svg + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css +``` + +| File | Purpose | +|---|---| +| `.svg-sprite/index.js` | Mode-specific production facade and runtime icon-name list | +| `.svg-sprite/index.d.ts` | Public declarations for the facade, component, and icon-name union | +| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata and the asset URL for `SpriteViewer` | +| `.svg-sprite/sprite.svg` | Compiled SVG sprite | +| `.svg-sprite/react/react-component.js` | React component runtime without TypeScript or JSX | +| `.svg-sprite/react/react-component.d.ts` | React component props, style, and declaration | +| `.svg-sprite/react/react-component.module.css` | Styles for the React implementation | +| `.svg-sprite/icon-data.js` | Runtime icon-name list and internal IDs | +| `.svg-sprite/*.d.ts` | TypeScript declarations for the corresponding JavaScript modules | + +Standalone contracts do not create `react/`. Bare `standalone` contains only the +runtime asset and deployment-neutral manifest data: + +```text +.svg-sprite/ +├── sprite.svg +└── svg-sprite.manifest.json +``` + +`standalone@vite` and `standalone@webpack` additionally create `index.*`, +`icon-data.*`, and a resolved `svg-sprite.manifest.*`. Their facade contains a +native generated Web Component with no external runtime dependencies. Bare +`standalone` intentionally does not generate a JavaScript component. + +The generator overwrites and deletes only files that contain its marker. If a user file occupies a managed path, generation fails. The root `index.ts` is user-owned; create a barrel when needed: + +```ts +export * from './.svg-sprite' +``` + +## Standalone Web Component and TypeScript + +In `standalone@vite` and `standalone@webpack`, a sprite with `name: 'app'` +exports the `defineAppIconElement()` registration function and the `` +tag: + +```ts +import { defineAppIconElement } from '@/ui/app-icons' + +defineAppIconElement() +``` + +After registration, use the element in HTML: + +```html + + + +``` + +The component renders `` in an open Shadow DOM, selects the internal +ID and `viewBox`, and obtains the asset URL through the corresponding Vite or +Webpack mechanism. The host defaults to `1em × 1em`; set `class`, `style`, +`color`, and `--icon-color-N` with ordinary CSS. + +The generated `HTMLElementTagNameMap` types the property API: + +```ts +const icon = document.createElement('app-icon') + +icon.icon = 'search' +icon.icon = 'unknown' // TypeScript error +``` + +TypeScript does not validate attribute values in plain HTML. Therefore an +unknown `icon="unknown"` is also validated at runtime: the component hides its +inner SVG and reports an error instead of creating a `#undefined` fragment. +Calling `defineAppIconElement()` repeatedly is safe for the same sprite; a +different element already registered as `` causes an error. + +## React component and TypeScript + +A sprite with `name: 'app'` exports: + +```ts +export { AppIcon, appIconNames } +export type { AppIconName, AppIconProps, AppIconStyle } +``` + +### Icon names + +SVG file names become valid `icon` values: + +```tsx + + // TypeScript error +``` + +The runtime list contains the same values: + +```ts +import { appIconNames } from '@/ui/app-icons' + +// readonly ['search', 'settings', 'user'] +``` + +Names containing spaces or other characters that are unsafe in SVG IDs remain part of the public API. For the internal fragment ID, the generator creates a stable, safe hash: + +```text +folder open.svg → icon="folder open" → id="icon-" +``` + +For these names, use the generated component or the `id` from the debug manifest instead of constructing the fragment ID manually. + +### SVG attributes + +By default, the component renders an `` and accepts standard SVG attributes: + +```tsx + +``` + +The component does not add accessibility semantics automatically. Pass appropriate `aria-*` attributes, a `role`, or a label based on the icon's purpose. + +### Wrapper + +`wrapped` renders a `` containing the SVG. In this mode, the remaining props apply to the ``: + +```tsx + +``` + +### Typed CSS custom properties + +`AppIconStyle` extends `CSSProperties` and supports properties in the form `--icon-color-N`: + +```tsx + +``` + +## Multiple sprites + +Each directory with a configuration creates an independent component, types, manifest, and SVG asset: + +```text +app-icons → AppIcon → shared icons +analytics-icons → AnalyticsIcon → analytics page icons +editor-icons → EditorIcon → editor icons +``` + +The same source SVG can be added to multiple configurations through `input`. You do not need to copy the file into each sprite directory. + +For multiple sprites, add a separate CLI command for each directory or combine the commands in a shared npm script. + +## Formats and rendering methods + +All current modes generate the `stack` format. + +| Format | `` | `` | CSS background | +|---|---:|---:|---:| +| `stack` | Yes | Yes | Yes | + +### Generated component + +For React and Next.js, use the generated React component. It knows the internal IDs, constructs the URL, and provides a TypeScript API: + +```tsx + +``` + +For `standalone@vite` and `standalone@webpack`, use the generated Web Component: + +```html + +``` + +### Manually with `` + +How you obtain `spriteUrl` depends on the bundler. + +Static HTML after the application publishes `.svg-sprite/sprite.svg`: + +```html + +``` + +Standalone Vite/Webpack provides generated `getIconsIconHref()` and an internal ID +map. Do not construct fragments from unsafe file names manually. + +Vite: + +```ts +import spriteUrl from './.svg-sprite/sprite.svg?no-inline' +``` + +Webpack 5, Turbopack, and Next.js: + +```ts +const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href +``` + +After obtaining the URL, use it in JSX: + +```tsx + + + +``` + +For names that are unsafe as SVG IDs, use the internal `id` from the manifest. + +### With `` + +```tsx +Search +``` + +An SVG inside `` is isolated from the page's CSS. Setting `color` or `--icon-color-N` on the outer element does not change its internal colors. + +### With CSS + +```css +.icon { + background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; +} +``` + +For a single-color silhouette, you can use a mask: + +```css +.icon { + background-color: currentColor; + mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; +} +``` + +A mask does not preserve original colors, gradients, or differences between `fill` and `stroke`. + +The path in CSS is resolved relative to the CSS file itself. In these examples, the CSS file is next to `svg-sprite.config.ts`. + +## Assets and caching + +The generated component or standalone facade passes the SVG to the bundler as a separate asset: + +- Vite uses a static import with `?no-inline`; +- Webpack 5, Turbopack, and Next.js use `new URL(..., import.meta.url)`; +- SVG path data is not serialized into generated JavaScript. + +Bare `standalone` does not participate in an asset pipeline: the application copies +or publishes `sprite.svg` and owns its URL, versioning, and cache policy. + +With standard asset naming, the bundler adds a content hash: + +```text +/assets/sprite-.svg +``` + +This allows the SVG to be cached separately from JavaScript. Changing React code does not change the sprite contents, while changing icons creates a new asset version. + +HTTP cache headers, CDN behavior, and `Cache-Control` are configured by the application or hosting platform. With Webpack, the final file name depends on the project's `assetModuleFilename`. + +## SVG transformations + +All transformations are enabled by default and can be configured independently: + +| Option | Behavior | +|---|---| +| `removeSize` | Removes `width` and `height` from the root `` while preserving an existing `viewBox` | +| `replaceColors` | Replaces detected `fill` and `stroke` values with `--icon-color-N` | +| `addTransition` | Adds transitions for `fill` and `stroke` to colored elements and generated styles | + +To disable an individual operation: + +```ts +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + transform: { + removeSize: false, + replaceColors: false, + addTransition: false, + }, +}) +``` + +Source SVG files are not modified. Transformations apply only to the generated sprite contents. + +## Color management + +### Monochrome icons + +If one color is detected, its fallback becomes `currentColor`: + +```svg +stroke="var(--icon-color-1, currentColor)" +``` + +Set the color through a prop or CSS: + +```tsx + +``` + +### Multicolor icons + +Each unique color gets its own custom property with the original color as its fallback: + +```svg +fill="var(--icon-color-1, #798198)" +fill="var(--icon-color-2, #ffffff)" +fill="var(--icon-color-3, #129d9d)" +``` + +You can override only the values you need: + +```css +.icon { + --icon-color-1: #4b5563; + --icon-color-3: #14b8a6; +} +``` + +### Limitations + +- `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced; +- colors in `fill`, `stroke`, and inline `style` attributes are handled most reliably; +- CSS classes and external stylesheets inside the SVG are not the primary transformation use case; +- `url(#...)` values may be replaced along with colors, so gradients and patterns require a separate sprite with `replaceColors: false`; +- masks, filters, and complex internal CSS rules require visual verification; +- page CSS custom properties are available through ``, but not inside `` or a CSS background. + +For a complex icon, you can disable `replaceColors` in a separate sprite configuration. + +## SpriteViewer + +The Viewer uses one Shadow DOM Web Component for every mode. React and future framework components are bridges to that same element, so the visuals and behavior are not duplicated. + +Bare `standalone` loads the self-contained browser bundle and supplies the JSON manifest URL and the published SVG URL: + +```html + + + +``` + +`viewer-element.js` has no additional runtime files and can be copied with the other static assets for self-hosting. + +`standalone@vite` and `standalone@webpack` register the same element through an npm entry and pass the generated JS manifest through the `sources` property: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './svg-sprite/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +React and Next.js keep the component API: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +``` + +It accepts ready-made manifests, remote standalone sources, an array of lazy loaders, or a record in the format returned by `import.meta.glob`. + +Vite: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' + +const sources = import.meta.glob( + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', +) + +export const IconsDebugPage = () => ( + +) +``` + +Webpack and Next.js: + +```tsx +const sources = [ + () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), + () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export const IconsDebugPage = () => ( + +) +``` + +The Viewer displays groups, search, `viewBox`, CSS custom properties, and fallback colors. React/Next manifests get React, SVG, IMG, and CSS tabs; standalone manifests get SVG, IMG, and CSS. You can change color values in the interface and immediately inspect the result. + +### Viewer theme + +By default, `colorTheme="auto"` follows `prefers-color-scheme`. You can explicitly pass `light` or `dark`: + +```tsx + +``` + +To synchronize it with the application theme: + +```tsx + +``` + +`@gromlab/svg-sprites/react` contains `'use client'` and renders the Web Component host; its internal Shadow DOM is created after the browser runtime loads. In the Next.js App Router, place the Viewer inside a separate Client Component boundary and use it only on a debug route or in an internal tool. + +## Generated files, Git, and CI + +Every mode except bare `standalone` creates a local `.gitignore` for: + +```text +/.svg-sprite/ +``` + +Commit the local `.gitignore` to the repository once. It excludes the other generated files, so generation must run before commands that import the sprite module: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "predev": "npm run sprites", + "prebuild": "npm run sprites", + "pretypecheck": "npm run sprites" + } +} +``` + +CI must run generation before building or type-checking. Replace `latest` with an exact version for reproducibility. A local package installation is not required unless CI also uses the Viewer, package config types, or the programmatic API. + +Bare `standalone` does not create or modify `.gitignore`; the application decides whether its `.svg-sprite/` output is committed or ignored. In other modes, the generator will not overwrite a user-created `.gitignore`. It also refuses to overwrite a user-owned file inside `.svg-sprite`. The root `index.ts` remains user-owned and may re-export the generated API. + +## Troubleshooting + +- Missing `.svg-sprite/index.js`: run the generation script before importing the generated module. +- Source not found: pass an existing config file or sprite module directory. +- Mode missing: add `mode` to the config or pass `--mode`. +- Icon missing from the type: check `input`, the `.svg` extension, glob exclusions, and whether nested folders require `**/*.svg`. +- Name conflict: two different SVG files have the same basename; rename one of them. +- `Refusing to overwrite a user file`: a file without the generated marker occupies a managed path. +- The icon does not change color: use `` or the generated component and check `replaceColors`. +- Webpack emits an incorrect URL: check Asset Modules, `output.publicPath`, and SVG loaders. +- Static sprite returns 404: check the post-generation copy or server alias, and do not put a filesystem `spritePath` into HTML. +- The Viewer cannot find the sprite: check the path to `.svg-sprite/svg-sprite.manifest.js` and run generation before starting the application. +- Build and mode do not match: use the target that corresponds to the actual bundler. + +For custom orchestration and low-level compilation, see the [Programmatic API](programmatic-api.md). diff --git a/docs/ru/README.md b/docs/ru/README.md new file mode 100644 index 0000000..e226abb --- /dev/null +++ b/docs/ru/README.md @@ -0,0 +1,29 @@ +# Документация + +Для настройки выберите guide одного exact mode. Каждый guide является +самостоятельным документом и без изменений используется в AI skills. + +## Гайды быстрого старта + +| Проект | Exact mode | Guide | +|---|---|---| +| Static HTML или собственная публикация | `standalone` | [Bare standalone](guides/standalone.md) | +| Vanilla + Vite | `standalone@vite` | [Standalone + Vite](guides/standalone-vite.md) | +| Vanilla + Webpack 5 | `standalone@webpack` | [Standalone + Webpack](guides/standalone-webpack.md) | +| React + Vite | `react@vite` | [React + Vite](guides/react-vite.md) | +| React + Webpack 5 | `react@webpack` | [React + Webpack](guides/react-webpack.md) | +| Next.js App Router + Turbopack | `next@app/turbopack` | [App Router + Turbopack](guides/next-app-turbopack.md) | +| Next.js App Router + Webpack | `next@app/webpack` | [App Router + Webpack](guides/next-app-webpack.md) | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | [Pages Router + Turbopack](guides/next-pages-turbopack.md) | +| Next.js Pages Router + Webpack | `next@pages/webpack` | [Pages Router + Webpack](guides/next-pages-webpack.md) | + +Все guides используют один порядок: + +1. Генерация спрайта через `npx` без добавления package в проект. +2. Необязательное подключение Viewer для дебага и превью. +3. Необязательная типизация конфига через package или локальный copy-paste type. + +## Справочники + +- [Технический справочник](reference/technical.md) +- [Программный API](reference/programmatic-api.md) diff --git a/docs/ru/guides/README.md b/docs/ru/guides/README.md new file mode 100644 index 0000000..e5788cc --- /dev/null +++ b/docs/ru/guides/README.md @@ -0,0 +1,11 @@ +# Гайды быстрого старта + +- `standalone`: [bare standalone](standalone.md) +- `standalone@vite`: [standalone с Vite](standalone-vite.md) +- `standalone@webpack`: [standalone с Webpack](standalone-webpack.md) +- `react@vite`: [React с Vite](react-vite.md) +- `react@webpack`: [React с Webpack](react-webpack.md) +- `next@app/turbopack`: [App Router с Turbopack](next-app-turbopack.md) +- `next@app/webpack`: [App Router с Webpack](next-app-webpack.md) +- `next@pages/turbopack`: [Pages Router с Turbopack](next-pages-turbopack.md) +- `next@pages/webpack`: [Pages Router с Webpack](next-pages-webpack.md) diff --git a/docs/ru/guides/next-app-turbopack.md b/docs/ru/guides/next-app-turbopack.md new file mode 100644 index 0000000..b56f876 --- /dev/null +++ b/docs/ru/guides/next-app-turbopack.md @@ -0,0 +1,153 @@ +# Next.js App Router с Turbopack + +Это автономный quick start для exact mode key `next@app/turbopack`: generated `IconsIcon` совместим с Server Components и asset pipeline Turbopack. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'next@app/turbopack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг может быть `.ts`, `.js` с `default export` или `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Mode и команды Next должны указывать один bundler: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --turbopack", + "build": "npm run sprites && next build --turbopack", + "start": "next start", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его `.d.ts` self-contained и не импортируют generator package. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Generated icon не содержит `'use client'`, поэтому его можно импортировать прямо в Server Component: + +```tsx +// app/page.tsx +import { IconsIcon, iconsIconNames } from '../src/sprite' + +export default function Page() { + return ( +
+ +
{iconsIconNames.length} иконок + + ) +} +``` + +Turbopack обрабатывает generated `new URL('../sprite.svg', import.meta.url).href` и выпускает внешний hashed asset. Не добавляйте Client Component boundary только ради `IconsIcon`. + +## 2. Дебаг и превью + +Viewer необязателен и нужен только для debug/preview: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Viewer интерактивен, поэтому создайте для него отдельный Client Component: + +```tsx +// app/icons-debug/sprite-viewer.tsx +'use client' + +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export function AppSpriteViewer() { + return +} +``` + +Server page импортирует только эту boundary: + +```tsx +// app/icons-debug/page.tsx +import { AppSpriteViewer } from './sprite-viewer' + +export default function IconsDebugPage() { + return +} +``` + +Viewer не входит в production icon runtime и не нужен обычным страницам с `IconsIcon`. + +## 3. Типизация конфига + +После локальной установки package используйте helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + name: 'icons', +}) +``` + +Возможен и type-only import `SpriteConfig` с `satisfies SpriteConfig`. + +Без package добавьте локальный type в config: + +```ts +type LocalSpriteConfig = { + mode: 'next@app/turbopack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@app/turbopack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Exact literal защищает от смешивания App Router и других bundler contracts. diff --git a/docs/ru/guides/next-app-webpack.md b/docs/ru/guides/next-app-webpack.md new file mode 100644 index 0000000..40d10b0 --- /dev/null +++ b/docs/ru/guides/next-app-webpack.md @@ -0,0 +1,151 @@ +# Next.js App Router с Webpack + +Это автономный quick start для exact mode key `next@app/webpack`: generated `IconsIcon` совместим с Server Components и Webpack pipeline Next.js. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'next@app/webpack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Также поддерживаются `.js` с `default export` и `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI замените `latest` точной версией, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для этого mode: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --webpack", + "build": "npm run sprites && next build --webpack", + "start": "next start", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не сочетайте явный `npm run sprites` с `predev` или `prebuild`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не зависят от `@gromlab/svg-sprites`. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage в Server Component: + +```tsx +// app/page.tsx +import { IconsIcon, iconsIconNames } from '../src/sprite' + +export default function Page() { + return ( +
+ + {iconsIconNames.length} иконок +
+ ) +} +``` + +Generated component не содержит `'use client'`. Next Webpack обрабатывает `new URL('../sprite.svg', import.meta.url).href` и публикует отдельный SVG asset; не переписывайте этот URL и не переносите sprite в `public` вручную. + +## 2. Дебаг и превью + +Viewer необязателен. Для debug/preview установите package: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +App Router требует отдельную Client Component boundary для Viewer: + +```tsx +// app/icons-debug/sprite-viewer.tsx +'use client' + +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export function AppSpriteViewer() { + return +} +``` + +```tsx +// app/icons-debug/page.tsx +import { AppSpriteViewer } from './sprite-viewer' + +export default function IconsDebugPage() { + return +} +``` + +Статический loader позволяет Webpack связать manifest и emitted SVG. Viewer не входит в production runtime `IconsIcon`. + +## 3. Типизация конфига + +При локально установленном package используйте helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/webpack', + name: 'icons', +}) +``` + +Другой package-вариант: type-only import `SpriteConfig` и `satisfies SpriteConfig`. + +Без package вставьте локальный type прямо в config: + +```ts +type LocalSpriteConfig = { + mode: 'next@app/webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@app/webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Локальный exact literal исключает случайную генерацию Turbopack или Pages Router output. diff --git a/docs/ru/guides/next-pages-turbopack.md b/docs/ru/guides/next-pages-turbopack.md new file mode 100644 index 0000000..b3e104a --- /dev/null +++ b/docs/ru/guides/next-pages-turbopack.md @@ -0,0 +1,140 @@ +# Next.js Pages Router с Turbopack + +Это автономный quick start для exact mode key `next@pages/turbopack`: generated `IconsIcon` работает при SSR, SSG и клиентских переходах Pages Router. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'next@pages/turbopack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +Для CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact commands должны сохранять Turbopack и для dev, и для production build: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --turbopack", + "build": "npm run sprites && next build --turbopack", + "start": "next start", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не добавляйте `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage на обычной page: + +```tsx +// pages/index.tsx +import { IconsIcon, iconsIconNames } from '../src/sprite' + +export default function Page() { + return ( +
+ + {iconsIconNames.length} иконок +
+ ) +} +``` + +Компонент одинаково работает с `getServerSideProps`, `getStaticProps` и client navigation. Turbopack разрешает generated `new URL('../sprite.svg', import.meta.url).href` в отдельный hashed asset. + +## 2. Дебаг и превью + +Viewer необязателен и нужен только для debug/preview: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +В Pages Router Viewer можно использовать прямо в page, без отдельной App Router Client Component boundary: + +```tsx +// pages/icons-debug.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export default function IconsDebugPage() { + return +} +``` + +Оставляйте эту page только во внутреннем debug-разделе. Viewer не входит в production icon runtime `IconsIcon`. + +## 3. Типизация конфига + +Если package установлен локально, используйте helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/turbopack', + name: 'icons', +}) +``` + +Альтернатива: type-only import `SpriteConfig` и объект `satisfies SpriteConfig`. + +Без package добавьте copy-paste type в config: + +```ts +type LocalSpriteConfig = { + mode: 'next@pages/turbopack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@pages/turbopack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Exact literal не позволяет незаметно смешать Pages Router с App Router или Webpack output. diff --git a/docs/ru/guides/next-pages-webpack.md b/docs/ru/guides/next-pages-webpack.md new file mode 100644 index 0000000..072b7a7 --- /dev/null +++ b/docs/ru/guides/next-pages-webpack.md @@ -0,0 +1,140 @@ +# Next.js Pages Router с Webpack + +Это автономный quick start для exact mode key `next@pages/webpack`: generated `IconsIcon` работает в Pages Router и публикует SVG через Webpack pipeline Next.js. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'next@pages/webpack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI замените `latest` на точную проверенную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для Webpack: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && next dev --webpack", + "build": "npm run sprites && next build --webpack", + "start": "next start", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не дублируйте эти вызовы через `predev`, `prebuild` или `pretypecheck`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не требуют `@gromlab/svg-sprites`. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage: + +```tsx +// pages/index.tsx +import { IconsIcon, iconsIconNames } from '../src/sprite' + +export default function Page() { + return ( +
+ + {iconsIconNames.length} иконок +
+ ) +} +``` + +Компонент поддерживает SSR, SSG и клиентские переходы. Next Webpack преобразует generated `new URL('../sprite.svg', import.meta.url).href` во внешний hashed asset; не конструируйте URL спрайта вручную. + +## 2. Дебаг и превью + +Viewer необязателен. Устанавливайте package только для debug/preview: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Pages Router позволяет разместить Viewer непосредственно в page без отдельной App Router boundary: + +```tsx +// pages/icons-debug.tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export default function IconsDebugPage() { + return +} +``` + +Статический loader даёт Webpack точный manifest module и связанный SVG asset. Не импортируйте Viewer из production pages, если preview там не нужен; runtime `IconsIcon` от него независим. + +## 3. Типизация конфига + +После локальной установки package доступен helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@pages/webpack', + name: 'icons', +}) +``` + +Также можно применить `satisfies SpriteConfig` с type-only импортом `SpriteConfig`. + +Без package вставьте локальный type прямо в config: + +```ts +type LocalSpriteConfig = { + mode: 'next@pages/webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'next@pages/webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Такой exact literal выявляет ошибочный выбор App Router или Turbopack ещё при проверке config. diff --git a/docs/ru/guides/react-vite.md b/docs/ru/guides/react-vite.md new file mode 100644 index 0000000..cedad15 --- /dev/null +++ b/docs/ru/guides/react-vite.md @@ -0,0 +1,141 @@ +# React-компонент для Vite + +Это автономный quick start для exact mode key `react@vite`: генератор создаёт типизированный `IconsIcon`, а Vite публикует отдельный SVG asset. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'react@vite', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Вместо `.ts` можно использовать `.js` с `default export` или `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Vite commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && vite", + "build": "npm run sprites && tsc --noEmit && vite build", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не добавляйте `predev`, `prebuild` или `pretypecheck`, если соответствующие scripts уже явно запускают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained: они описывают компонент и manifest без импорта `@gromlab/svg-sprites`. + +Пользовательский barrel возвращает generated API: + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage: + +```tsx +import { IconsIcon, iconsIconNames } from './sprite' + +export function SaveButton() { + return ( + + ) +} + +console.log(iconsIconNames) +``` + +Prop `icon` является union имён исходных файлов. Vite автоматически обрабатывает generated CSS Module и импорт `sprite.svg?no-inline`; query запрещает inline и заставляет Vite выпустить отдельный hashed SVG asset. Если TypeScript не знает Vite asset imports, добавьте `/// ` в `src/vite-env.d.ts`. + +## 2. Дебаг и превью + +Viewer необязателен и нужен только для debug/preview. Установите package отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Используйте React bridge со статическим массивом loaders: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export function IconsDebugPage() { + return +} +``` + +Строковый путь в `import()` должен указывать на generated JS manifest. Держите страницу за debug-маршрутом; `SpriteViewer` не входит в production runtime `IconsIcon`. + +## 3. Типизация конфига + +Если package установлен локально, используйте helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'icons', +}) +``` + +Также можно импортировать `SpriteConfig` только как type и написать объект `satisfies SpriteConfig`. + +Без package добавьте локальный type прямо в config: + +```ts +type LocalSpriteConfig = { + mode: 'react@vite' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'react@vite', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Локальный type проверяет только этот exact mode и не создаёт runtime-зависимость. diff --git a/docs/ru/guides/react-webpack.md b/docs/ru/guides/react-webpack.md new file mode 100644 index 0000000..a7591a2 --- /dev/null +++ b/docs/ru/guides/react-webpack.md @@ -0,0 +1,141 @@ +# React-компонент для Webpack 5 + +Это автономный quick start для exact mode key `react@webpack`: generated `IconsIcon` использует Webpack 5 Asset Modules и CSS Modules. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'react@webpack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && webpack serve --mode development", + "build": "npm run sprites && webpack --mode production", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не сочетайте эти явные вызовы с `predev`/`prebuild`: иначе генерация задублируется. `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage: + +```tsx +import { IconsIcon, iconsIconNames } from './sprite' + +export function SaveButton() { + return ( + + ) +} + +console.log(iconsIconNames) +``` + +Generated component получает URL через `new URL('../sprite.svg', import.meta.url).href`. Webpack 5 должен обработать SVG как Asset Module. Исключите generated `sprite.svg` из `@svgr/webpack`, inline/raw loaders и других общих SVG rules либо задайте для него `type: 'asset/resource'`. + +Компонент импортирует `react-component.module.css`. Webpack config должен обрабатывать `*.module.css` через `css-loader` с CSS Modules и `style-loader` или `MiniCssExtractPlugin`. Для TypeScript при необходимости добавьте декларацию `declare module '*.module.css'`. + +## 2. Дебаг и превью + +Viewer необязателен. Устанавливайте package только для debug/preview: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Webpack не использует `import.meta.glob`; передайте статический loader: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' + +const sources = [ + () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), +] as const + +export function IconsDebugPage() { + return +} +``` + +Webpack создаст chunk manifest и разрешит его SVG через тот же Asset Modules pipeline. Viewer держите только в debug route; production `IconsIcon` от него не зависит. + +## 3. Типизация конфига + +С локально установленным package доступен helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@webpack', + name: 'icons', +}) +``` + +Эквивалентная проверка: type-only import `SpriteConfig` и `satisfies SpriteConfig`. + +Без package используйте copy-paste type в config: + +```ts +type LocalSpriteConfig = { + mode: 'react@webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'react@webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Локальный literal не разрешит случайно выбрать Vite или Next mode. diff --git a/docs/ru/guides/standalone-vite.md b/docs/ru/guides/standalone-vite.md new file mode 100644 index 0000000..03e53fe --- /dev/null +++ b/docs/ru/guides/standalone-vite.md @@ -0,0 +1,151 @@ +# Нативный icon Web Component с Vite + +Это автономный quick start для exact mode key `standalone@vite`: generated facade регистрирует `` и отдаёт SVG в asset pipeline Vite. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +Рекомендуемая структура держит конфиг и иконки рядом: + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный plain config: + +```ts +export default { + mode: 'standalone@vite', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact dev/build commands для Vite: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && vite", + "build": "npm run sprites && vite build", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites` в этих scripts. `.svg-sprite` является generated-каталогом и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют `@gromlab/svg-sprites`. + +Верните facade через пользовательский barrel: + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production entry регистрирует native Web Component: + +```ts +import { defineIconsIconElement, iconsIconNames } from './sprite' + +defineIconsIconElement() + +document.querySelector('#app')!.innerHTML = ` + +` + +console.log(iconsIconNames) +``` + +Generated facade импортирует `sprite.svg?no-inline`: Vite автоматически выпускает отдельный hashed SVG asset и не превращает его в data URL. TypeScript-проекту при необходимости добавьте стандартные Vite types: + +```ts +/// +``` + +Размер по умолчанию равен `1em`, поэтому компонент удобно масштабировать через `font-size`. Цвет задаётся через `color` и generated custom properties: + +```css +icons-icon { + font-size: 24px; + color: #334155; + --icon-color-2: #f59e0b; +} +``` + +## 2. Дебаг и превью + +Viewer необязателен и нужен только для debug/preview. Установите его отдельно: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Зарегистрируйте Viewer и передайте generated JS manifest через свойство `sources`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('#app')!.insertAdjacentHTML( + 'beforeend', + '', +) + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.viewerTitle = 'Иконки проекта' +viewer.sources = [spriteManifest] +``` + +Расположите этот код только в debug entry или внутреннем маршруте. Viewer не входит в production runtime ``. + +## 3. Типизация конфига + +При локально установленном package используйте helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'standalone@vite', + name: 'icons', +}) +``` + +Альтернатива с package: `import type { SpriteConfig }` и объект `satisfies SpriteConfig`. + +Без package скопируйте локальный type прямо в config: + +```ts +type LocalSpriteConfig = { + mode: 'standalone@vite' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'standalone@vite', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Локальный type ограничивает `mode` одним exact literal и ничего не загружает во время генерации. diff --git a/docs/ru/guides/standalone-webpack.md b/docs/ru/guides/standalone-webpack.md new file mode 100644 index 0000000..f60ca05 --- /dev/null +++ b/docs/ru/guides/standalone-webpack.md @@ -0,0 +1,145 @@ +# Нативный icon Web Component с Webpack 5 + +Это автономный quick start для exact mode key `standalone@webpack`: generated facade предоставляет ``, а Webpack 5 публикует SVG через Asset Modules. + +## 1. Генерация спрайта + +Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. + +```text +src/sprite/ +├── icons/ +│ ├── check.svg +│ └── warning.svg +├── index.ts +└── svg-sprite.config.ts +``` + +Минимальный config рядом с `icons/`: + +```ts +export default { + mode: 'standalone@webpack', + name: 'icons', +} +``` + +Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются также `.js` с `default export` и `.json`. + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts +``` + +Для CI закрепите точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", + "dev": "npm run sprites && webpack serve --mode development", + "build": "npm run sprites && webpack --mode production", + "typecheck": "npm run sprites && tsc --noEmit" + } +} +``` + +Не дублируйте запуск через `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его declarations self-contained и не требуют `@gromlab/svg-sprites`. + +```ts +// src/sprite/index.ts +export * from './.svg-sprite/index.js' +``` + +Production usage: + +```ts +import { defineIconsIconElement, iconsIconNames } from './sprite' + +defineIconsIconElement() + +document.querySelector('#app')!.innerHTML = ` + +` + +console.log(iconsIconNames) +``` + +Generated facade использует `new URL('./sprite.svg', import.meta.url).href`. Webpack 5 Asset Modules выпускают отдельный asset; его итоговый URL учитывает `output.publicPath` и `assetModuleFilename`. + +Если проект использует `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общий SVG rule, исключите `src/sprite/.svg-sprite/sprite.svg` из этого правила. Generated SVG должен обрабатываться как `asset/resource`, а не как React-компонент или inline source. + +Размер Web Component по умолчанию `1em`; управляйте им и цветами обычным CSS: + +```css +icons-icon { + font-size: 24px; + color: #334155; + --icon-color-2: #f59e0b; +} +``` + +## 2. Дебаг и превью + +Viewer необязателен. Для debug/preview установите package как dev dependency: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Подключите element entry и generated JS manifest: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' + +document.querySelector('#app')!.insertAdjacentHTML( + 'beforeend', + '', +) + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.viewerTitle = 'Иконки проекта' +viewer.sources = [spriteManifest] +``` + +Webpack свяжет manifest с тем же emitted SVG asset. Оставляйте Viewer только в debug entry: production `` от него не зависит. + +## 3. Типизация конфига + +После локальной установки package можно использовать helper: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'standalone@webpack', + name: 'icons', +}) +``` + +Либо импортируйте только `SpriteConfig` как type и примените `satisfies SpriteConfig`. + +Без package добавьте copy-paste type в сам config: + +```ts +type LocalSpriteConfig = { + mode: 'standalone@webpack' + name?: string + description?: string + input?: string | string[] + transform?: { + removeSize?: boolean + replaceColors?: boolean + addTransition?: boolean + } + generatedNotice?: boolean +} + +export default { + mode: 'standalone@webpack', + name: 'icons', +} satisfies LocalSpriteConfig +``` + +Этот вариант сохраняет проверку exact mode без runtime import и без записи generator package в проект. diff --git a/docs/ru/guides/standalone.md b/docs/ru/guides/standalone.md new file mode 100644 index 0000000..bf36246 --- /dev/null +++ b/docs/ru/guides/standalone.md @@ -0,0 +1,108 @@ +# SVG-спрайт для сайта без сборщика + +Соберите SVG-иконки в один файл и используйте их на HTML-странице. + +## Генерация спрайта + +Устанавливать пакет в проект не нужно. + +В руководстве используется следующая структура проекта: + +```text +/ +├── index.html +└── assets/ + ├── app-icons/ + └── svg-icons/ + ├── check.svg + └── warning.svg +``` + +### 1. Создайте конфиг спрайта + +Выберите папку для спрайта. В этом примере используется `assets/app-icons`. Создайте в ней файл `svg-sprite.config.json`: + +```json +{ + "mode": "standalone", + "name": "icons" +} +``` + +### 2. Укажите источник иконок + +В `input` можно указать папку, отдельный SVG-файл или glob-шаблон. Для нескольких источников используйте массив с любой комбинацией этих значений: + +```json +{ + "mode": "standalone", + "name": "icons", + "input": "../svg-icons/**/*.svg" +} +``` + +### 3. Сгенерируйте спрайт + +Передайте команде путь к конфигу: + +```bash +npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json +``` + +Пакет соберёт иконки в каталог `.svg-sprite` рядом с конфигом: + +```text +assets/app-icons/.svg-sprite/ +├── sprite.svg +└── svg-sprite.manifest.json +``` + +- `sprite.svg` — готовый спрайт для использования на сайте. +- `svg-sprite.manifest.json` — данные об иконках для Viewer. + +Каталог `.svg-sprite` создаётся автоматически и полностью заменяется при каждой генерации. Не редактируйте его содержимое вручную. + +### 4. Используйте иконку + +В `index.html` укажите путь к созданному `sprite.svg`. После `#` добавьте имя нужной иконки без расширения `.svg`: + +```html + + + +``` + +Иконка из файла `check.svg` будет доступна как `#check`. + +## Дебаг и превью + +`sprite.svg` — технический файл, а не галерея иконок. При его открытии нельзя удобно просмотреть весь набор. Кроме того, градиенты, маски, фильтры и ссылки на внутренние `id` могут отображаться с артефактами. + +Для визуальной проверки используйте официальный Viewer. Он показывает все иконки спрайта и помогает проверить их цвета и отображение. + +Viewer необязателен и предназначен только для разработки. Устанавливать пакет через npm не нужно. + +Viewer работает напрямую с файлами из `.svg-sprite`. Ничего копировать не нужно. + +### Добавьте Viewer на страницу + +Добавьте в `index.html` module script и укажите пути к generated manifest и спрайту: + +```html + + + +``` + +Viewer можно вынести в отдельный HTML-файл в корне сайта, предназначенный только для разработки и проверки иконок. \ No newline at end of file diff --git a/docs/ru/next-app.md b/docs/ru/next-app.md index 653b1b6..9b8acea 100644 --- a/docs/ru/next-app.md +++ b/docs/ru/next-app.md @@ -1,113 +1,4 @@ -# Next.js App Router +# Guides Next.js App Router перемещены -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | -|---|---| -| Turbopack | `next@app/turbopack` | -| Webpack 5 | `next@app/webpack` | - -## 1. Установите пакет - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -Корневой barrel принадлежит приложению: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -## 3. Добавьте генерацию - -Для Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Webpack замените mode key на `next@app/webpack`. - -До импорта generated-модуля выполните первую генерацию: - -```bash -npm run sprite:file-manager -``` - -## 4. Используйте в Server Component - -Generated-компонент не содержит `'use client'`, поэтому его можно импортировать непосредственно в `page.tsx` или `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js выпустит отдельный SVG asset с content hash. Один generated-код используется при SSR и в браузере без расхождения URL. - -## 5. Добавьте SpriteViewer - -Viewer интерактивен, поэтому для него нужна отдельная Client Component граница: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -Запустите build script проекта, настроенный на выбранный сборщик: - -```bash -npm run build -``` - -Build script и mode key генератора должны указывать один и тот же сборщик. +- [App Router + Turbopack](guides/next-app-turbopack.md) +- [App Router + Webpack](guides/next-app-webpack.md) diff --git a/docs/ru/next-pages.md b/docs/ru/next-pages.md index bd6526a..07429da 100644 --- a/docs/ru/next-pages.md +++ b/docs/ru/next-pages.md @@ -1,105 +1,4 @@ -# Next.js Pages Router +# Guides Next.js Pages Router перемещены -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | -|---|---| -| Turbopack | `next@pages/turbopack` | -| Webpack 5 | `next@pages/webpack` | - -## 1. Установите пакет - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/webpack', - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -Корневой barrel принадлежит приложению: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -## 3. Добавьте генерацию - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Turbopack замените mode key на `next@pages/turbopack`. - -До импорта generated-модуля выполните первую генерацию: - -```bash -npm run sprite:file-manager -``` - -## 4. Используйте на странице - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -Компонент одинаково работает при SSR, SSG и клиентских переходах. Next.js выпускает отдельный SVG asset с content hash. - -## 5. Добавьте SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -Запустите build script проекта, настроенный на выбранный сборщик: - -```bash -npm run build -``` - -Build script и mode key генератора должны указывать один и тот же сборщик. +- [Pages Router + Turbopack](guides/next-pages-turbopack.md) +- [Pages Router + Webpack](guides/next-pages-webpack.md) diff --git a/docs/ru/programmatic-api.md b/docs/ru/programmatic-api.md index 5a3c559..b5e68be 100644 --- a/docs/ru/programmatic-api.md +++ b/docs/ru/programmatic-api.md @@ -1,147 +1,3 @@ -# Программный API +# Программный API перемещён -[← Главная](../../README_RU.md) - -Пакет распространяется как ESM и предоставляет единый Node.js API генерации. Framework-neutral Viewer находится в `@gromlab/svg-sprites/viewer`, auto-register entry — в `@gromlab/svg-sprites/viewer/element`, React bridge — в `@gromlab/svg-sprites/react`. - -## `generateSprite` - -```ts -import { generateSprite } from '@gromlab/svg-sprites' - -const result = await generateSprite( - 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', -) -``` - -Для static standalone mode `result.spritePath` можно использовать в build-скрипте, -чтобы опубликовать SVG по URL приложения: - -```ts -import { copyFile } from 'node:fs/promises' - -const result = await generateSprite('src/sprite/svg-sprite.config.ts', { - mode: 'standalone', -}) -await copyFile(result.spritePath, 'dist/app-icons/sprite.svg') -``` - -`spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON -manifest доступен через `result.manifestPath` и копируется независимо от SVG. - -Первый аргумент принимает полный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог. - -Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом: - -```ts -await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { - mode: 'react@webpack', - name: 'documents', - inputFolder: './assets', - inputFiles: ['../../shared/search.svg'], - transform: { - addTransition: false, - }, - generatedNotice: false, -}) -``` - -Порядок разрешения настроек: - -```text -defaults → config → API overrides -``` - -Для полностью программной генерации передайте каталог и все обязательные настройки через overrides: - -```ts -await generateSprite('src/ui/file-manager/svg-sprite', { - mode: 'react@vite', - name: 'file-manager', - inputFiles: [ - '../../shared/search.svg', - '../../shared/settings.svg', - ], -}) -``` - -## Конфигурация - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../shared/check.svg'], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`defineSpriteConfig` является identity helper для TypeScript autocomplete. JS может экспортировать тот же объект через `export default`, а JSON содержит объект непосредственно. - -## Специализированные обёртки - -Специализированные функции доступны как обёртки над `generateSprite`: - -```ts -import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' - -await generateReactSprite('path/to/config.ts', 'vite') -await generateNextSprite('path/to/config.ts', { - router: 'app', - bundler: 'turbopack', -}) -``` - -Явно переданный target перекрывает `mode` из файла. Для нового кода используйте `generateSprite`. - -## Config API - -```ts -import { - loadSpriteConfig, - resolveSpriteConfig, - validateSpriteConfig, -} from '@gromlab/svg-sprites' -``` - -- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл. -- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта. -- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`. - -## Низкоуровневый compiler - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, -} from '@gromlab/svg-sprites' -``` - -Эти функции предназначены для собственного orchestration. Стандартная генерация должна выполняться через `generateSprite`. - -## Viewer runtime - -```ts -import '@gromlab/svg-sprites/viewer/element' -import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -``` - -Browser entry регистрирует ``. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler. - -React bridge сохраняет компонентный API: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -`SpriteViewer` принимает generated manifests, remote standalone sources, lazy loaders или результат `import.meta.glob`. React entry содержит `'use client'` и предназначен для debug-инструментов; production-компоненты импортируются из локальных sprite-модулей приложения. +Canonical документ: [программный API](reference/programmatic-api.md). diff --git a/docs/ru/react-vite.md b/docs/ru/react-vite.md index 59b7bb0..0a92e9b 100644 --- a/docs/ru/react-vite.md +++ b/docs/ru/react-vite.md @@ -1,125 +1,3 @@ -# React + Vite +# Guide React + Vite перемещён -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Vite. - -В результате вы получите типизированный React-компонент и отдельный кешируемый SVG asset. - -## 1. Установите пакет - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -Корневой barrel принадлежит приложению и явно возвращает generated API: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [«Единая конфигурация»](reference.md#единая-конфигурация). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -Имя `file-manager` преобразуется в `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript). - -Vite выпустит спрайт отдельным файлом вида `assets/sprite-.svg`. SVG path-данные не попадут в JavaScript. - -## 6. Добавьте debug-страницу - -После подключения иконок можно вывести все React-спрайты через `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite автоматически найдёт generated manifest каждого React-спрайта. Шаблон `import.meta.glob` должен быть строковым литералом, а генерация должна выполниться до запуска Vite. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `.svg-sprite/index.js`: запустите `npm run sprite:file-manager`. -- Viewer не видит спрайт: проверьте glob-путь к `.svg-sprite/svg-sprite.manifest.js`. -- Ошибка `Refusing to overwrite a user file`: в generated-пути находится пользовательский файл. -- Иконка не меняет цвет: используйте `color` или `--icon-color-N`. +Canonical guide: [React + Vite](guides/react-vite.md). diff --git a/docs/ru/react-webpack.md b/docs/ru/react-webpack.md index 327afba..b47ded9 100644 --- a/docs/ru/react-webpack.md +++ b/docs/ru/react-webpack.md @@ -1,129 +1,3 @@ -# React + Webpack 5 +# Guide React + Webpack перемещён -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Webpack 5. - -В результате вы получите типизированный React-компонент и отдельный SVG asset через Webpack Asset Modules. - -## 1. Установите пакет - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -├── index.ts -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@webpack', - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -Корневой barrel принадлежит приложению и явно возвращает generated API: - -```ts -// src/ui/file-manager/svg-sprite/index.ts -export * from './.svg-sprite' -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [«Единая конфигурация»](reference.md#единая-конфигурация). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [техническом справочнике](reference.md#react-компонент-и-typescript). - -Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset. - -Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules. - -Generated-компонент импортирует `react/react-component.module.css`, поэтому Webpack должен обрабатывать CSS Modules через `css-loader` и `style-loader` либо `MiniCssExtractPlugin`. Если TypeScript-проект не содержит декларации для CSS Modules, добавьте её отдельно. - -## 6. Добавьте debug-страницу - -Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), - () => import('./ui/navigation/svg-sprite/.svg-sprite/svg-sprite.manifest.js'), -] - -export const IconsDebugPage = () => ( - -) -``` - -Пути в `import()` должны быть строковыми литералами. Webpack создаст chunks для манифестов и свяжет их с SVG assets. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `.svg-sprite/index.js`: запустите `npm run sprite:file-manager`. -- Viewer не загружает спрайт: проверьте путь в `import()` к `.svg-sprite/svg-sprite.manifest.js`. -- Неверный URL asset: проверьте `output.publicPath`. -- SVG перехватывает другой loader: исключите generated sprite из несовместимого правила. - -Для Next.js используйте отдельные mode key из руководств [App Router](next-app.md) и [Pages Router](next-pages.md). +Canonical guide: [React + Webpack](guides/react-webpack.md). diff --git a/docs/ru/reference.md b/docs/ru/reference.md index ce66bd1..caa74ab 100644 --- a/docs/ru/reference.md +++ b/docs/ru/reference.md @@ -1,555 +1,3 @@ -# Технический справочник +# Технический справочник перемещён -[← Главная](../../README_RU.md) - -Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека: - -- [Next.js App Router](next-app.md) -- [Next.js Pages Router](next-pages.md) -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) - -## Требования - -- Node.js 18 или новее; -- пакет распространяется как ESM и подключается через `import`; -- React 18 или 19 требуется для generated-компонентов и `@gromlab/svg-sprites/react`; -- для типизации package exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -Пакет устанавливается как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -## CLI и режимы генерации - -CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации: - -```text -svg-sprites [options] -``` - -| Среда | Mode | -|---|---| -| Static HTML / собственная публикация | `standalone` | -| Standalone + Vite | `standalone@vite` | -| Standalone + Webpack 5 | `standalone@webpack` | -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | - -Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.ts`. - -Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`. - -Доступны `--mode`, `--name`, `--description`, `--input-folder`, повторяемый `--input-file`, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input-file` заменяет весь массив `inputFiles` из config. - -Mode должен соответствовать способу публикации приложения. Bare `standalone` оставляет публичный URL приложению; Vite и Webpack modes генерируют bundler-specific подключение SVG asset. - -## Единая конфигурация - -Каждый config-файл описывает один независимый спрайт. - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'app', - description: 'Общие иконки приложения', - inputFolder: './local-icons', - inputFiles: [ - '../../assets/icons/search.svg', - '../../assets/icons/settings.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Опция | Тип | По умолчанию | Назначение | -|---|---|---|---| -| `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API | -| `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов | -| `description` | `string` | Нет | Описание для типов и debug manifest | -| `inputFolder` | `string` | `./icons` | Каталог с SVG относительно корня модуля | -| `inputFiles` | `string[]` | `[]` | Пути к отдельным SVG относительно корня модуля | -| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG | -| `generatedNotice` | `boolean` | `true` | Полное или короткое предупреждение в generated-файлах | - -### Имя спрайта - -`name` записывается в kebab-case и должно начинаться с латинской буквы: - -```text -app → AppIcon -file-manager → FileManagerIcon -``` - -Если `name` не задано, генератор выводит его из каталога. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога. - -### Источники иконок - -`inputFolder` и `inputFiles` объединяются в один набор. Это позволяет хранить локальные SVG рядом с модулем и добавлять общие иконки из других частей проекта без копирования. - -Если `inputFiles` заполнен, а неявного каталога `./icons` нет, генерация работает только по списку файлов. Явно указанная отсутствующая `inputFolder` считается ошибкой. - -Каталог сканируется только на первом уровне. Вложенные каталоги рекурсивно не обходятся. Для вложенной структуры перечислите точные пути через `inputFiles`. - -Одинаковые абсолютные пути дедуплицируются. Разные SVG с одинаковым именем файла считаются конфликтом, потому что публичное имя иконки выводится из basename. - -## Generated-модуль - -После генерации React- или Next.js-каталог спрайта выглядит так: - -```text -app-icons/ -├── .gitignore -├── svg-sprite.config.ts -├── index.ts # необязательный пользовательский barrel -└── .svg-sprite/ - ├── state.json - ├── index.js - ├── index.d.ts - ├── icon-data.js - ├── icon-data.d.ts - ├── sprite.svg - ├── svg-sprite.manifest.js - ├── svg-sprite.manifest.d.ts - └── react/ - ├── react-component.js - ├── react-component.d.ts - └── react-component.module.css -``` - -| Файл | Назначение | -|---|---| -| `.svg-sprite/index.js` | Production exports компонента и runtime-списка имён | -| `.svg-sprite/index.d.ts` | Публичные декларации компонента, props, стилей и union-типа имён | -| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata и URL asset для `SpriteViewer` | -| `.svg-sprite/sprite.svg` | Собранный SVG-спрайт | -| `.svg-sprite/react/react-component.js` | Runtime React-компонента без TypeScript и JSX | -| `.svg-sprite/react/react-component.d.ts` | Props, style и declaration React-компонента | -| `.svg-sprite/react/react-component.module.css` | Стили конкретной React-реализации | -| `.svg-sprite/icon-data.js` | Runtime-список имён и внутренние IDs | -| `.svg-sprite/*.d.ts` | TypeScript-декларации соответствующих JS-модулей | -| `.svg-sprite/state.json` | Mode, версия контракта и список управляемых файлов | - -Standalone-контракты не создают каталог `react/`. Bare `standalone` содержит только -runtime asset и deployment-neutral manifest data: - -```text -.svg-sprite/ -├── state.json -├── sprite.svg -└── svg-sprite.manifest.json -``` - -`standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, -`icon-data.*` и resolved `svg-sprite.manifest.*`. - -Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой `index.ts` генератору не принадлежит; при необходимости создайте пользовательский barrel: - -```ts -export * from './.svg-sprite' -``` - -## React-компонент и TypeScript - -Спрайт с `name: 'app'` экспортирует: - -```ts -export { AppIcon, appIconNames } -export type { AppIconName, AppIconProps, AppIconStyle } -``` - -### Имена иконок - -Имена SVG-файлов становятся допустимыми значениями `icon`: - -```tsx - - // ошибка TypeScript -``` - -Runtime-список содержит те же значения: - -```ts -import { appIconNames } from '@/ui/app-icons' - -// readonly ['search', 'settings', 'user'] -``` - -Имена с пробелами и другими небезопасными для SVG ID символами остаются частью публичного API. Для внутреннего fragment ID генератор создаёт стабильный безопасный hash: - -```text -folder open.svg → icon="folder open" → id="icon-" -``` - -Для таких имён используйте generated-компонент или `id` из debug manifest, а не формируйте fragment ID вручную. - -### SVG-атрибуты - -По умолчанию компонент рендерит `` и принимает стандартные SVG-атрибуты: - -```tsx - -``` - -Компонент не добавляет accessibility-семантику автоматически. Передавайте подходящие `aria-*`, `role` или подпись в зависимости от назначения иконки. - -### Обёртка - -`wrapped` рендерит `` с внутренним SVG. Остальные props в этом режиме относятся к ``: - -```tsx - -``` - -### Типизированные CSS-переменные - -`AppIconStyle` расширяет `CSSProperties` и поддерживает свойства вида `--icon-color-N`: - -```tsx - -``` - -## Множественные спрайты - -Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset: - -```text -app-icons → AppIcon → общие иконки -analytics-icons → AnalyticsIcon → иконки страницы аналитики -editor-icons → EditorIcon → иконки редактора -``` - -Один исходный SVG можно добавить через `inputFiles` в несколько конфигураций. Копировать файл в каталоги каждого спрайта не требуется. - -Для нескольких спрайтов добавьте отдельную CLI-команду для каждого каталога или объедините команды в общем npm script. - -## Форматы и способы отображения - -Все текущие modes создают формат `stack`. - -| Формат | `` | `` | CSS background | -|---|---:|---:|---:| -| `stack` | Да | Да | Да | - -### Generated-компонент - -Для React и Next.js используйте generated-компонент. Он знает внутренние ID, формирует URL и предоставляет TypeScript API: - -```tsx - -``` - -### Вручную через `` - -Способ получения `spriteUrl` зависит от сборщика. - -Static HTML после публикации `.svg-sprite/sprite.svg` приложением: - -```html - -``` - -Standalone Vite/Webpack предоставляет generated `getIconsIconHref()` и mapping -внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную. - -Vite: - -```ts -import spriteUrl from './.svg-sprite/sprite.svg?no-inline' -``` - -Webpack 5, Turbopack и Next.js: - -```ts -const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href -``` - -После получения URL используйте его в JSX: - -```tsx - - - -``` - -Для имён, небезопасных как SVG ID, используйте внутренний `id` из manifest. - -### Через `` - -```tsx -Поиск -``` - -SVG внутри `` изолирован от CSS страницы. `color` и `--icon-color-N` на внешнем элементе не изменяют его внутренние цвета. - -### Через CSS - -```css -.icon { - background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; -} -``` - -Для одноцветного силуэта можно использовать mask: - -```css -.icon { - background-color: currentColor; - mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; -} -``` - -Mask не сохраняет исходные цвета, gradients и различия между `fill` и `stroke`. - -Путь в CSS разрешается относительно самого CSS-файла. В примерах CSS-файл находится рядом с `svg-sprite.config.ts`. - -## Assets и кеширование - -Generated component или standalone facade передаёт SVG сборщику как отдельный asset: - -- Vite использует статический импорт с `?no-inline`; -- Webpack 5, Turbopack и Next.js используют `new URL(..., import.meta.url)`; -- SVG path-данные не сериализуются в generated JavaScript. - -Bare `standalone` не участвует в asset pipeline: приложение само копирует или -публикует `sprite.svg` и отвечает за URL, версионирование и cache policy. - -При стандартном именовании assets сборщик добавляет content hash: - -```text -/assets/sprite-.svg -``` - -Это позволяет кешировать SVG отдельно от JavaScript. Изменение React-кода не меняет содержимое спрайта, а изменение иконок создаёт новую версию asset. - -HTTP cache headers, CDN и `Cache-Control` настраиваются приложением или платформой размещения. Для Webpack имя итогового файла зависит от `assetModuleFilename` проекта. - -## Трансформации SVG - -Все трансформации включены по умолчанию и настраиваются независимо: - -| Опция | Что делает | -|---|---| -| `removeSize` | Удаляет `width` и `height` с корневого ``, сохраняя существующий `viewBox` | -| `replaceColors` | Заменяет найденные `fill` и `stroke` на `--icon-color-N` | -| `addTransition` | Добавляет transitions для `fill` и `stroke` в цветные элементы и generated styles | - -Чтобы отключить отдельную операцию: - -```ts -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - transform: { - removeSize: false, - replaceColors: false, - addTransition: false, - }, -}) -``` - -Исходные SVG не изменяются. Трансформации применяются только к содержимому generated-спрайта. - -## Управление цветами - -### Монохромные иконки - -Если найден один цвет, fallback становится `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -Цвет задаётся через prop или CSS: - -```tsx - -``` - -### Многоцветные иконки - -Каждый уникальный цвет получает отдельную переменную с исходным fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -Можно заменить только необходимые значения: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Ограничения - -- `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются; -- надёжнее всего обрабатываются цвета в атрибутах `fill`, `stroke` и inline `style`; -- CSS-классы и внешние stylesheets внутри SVG не являются основным сценарием трансформации; -- значения `url(#...)` могут быть заменены вместе с цветами, поэтому gradients и patterns требуют отдельного спрайта с `replaceColors: false`; -- masks, filters и сложные внутренние CSS-правила требуют визуальной проверки; -- CSS-переменные страницы доступны через ``, но не внутри `` и CSS background. - -Для сложной иконки можно отключить `replaceColors` в конфигурации отдельного спрайта. - -## SpriteViewer - -Viewer использует один Web Component с Shadow DOM для всех modes. React и будущие framework-компоненты являются bridge к этому же элементу, поэтому визуал и поведение не дублируются. - -Bare `standalone` подключает самостоятельный browser bundle и передаёт URL JSON manifest и опубликованного SVG: - -```html - - - -``` - -`viewer-element.js` не имеет дополнительных runtime-файлов и может быть скопирован с остальными static assets для self-hosting. - -`standalone@vite` и `standalone@webpack` регистрируют тот же элемент через npm entry и передают generated JS manifest через свойство `sources`: - -```ts -import '@gromlab/svg-sprites/viewer/element' -import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -import spriteManifest from './svg-sprite/.svg-sprite/svg-sprite.manifest.js' - -const viewer = document.querySelector('gromlab-sprite-viewer')! -viewer.sources = [spriteManifest] -``` - -React и Next.js сохраняют компонентный API: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -Он принимает готовые manifests, remote standalone sources, массив lazy loaders или record формата `import.meta.glob`. - -Vite: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export const IconsDebugPage = () => ( - -) -``` - -Webpack и Next.js: - -```tsx -const sources = [ - () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), - () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), -] - -export const IconsDebugPage = () => ( - -) -``` - -Viewer показывает группы, поиск, `viewBox`, CSS-переменные и fallback-цвета. React/Next manifests получают вкладки React, SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат. - -### Тема Viewer - -По умолчанию `colorTheme="auto"` следует `prefers-color-scheme`. Можно передать `light` или `dark` явно: - -```tsx - -``` - -Для синхронизации с темой приложения: - -```tsx - -``` - -`@gromlab/svg-sprites/react` содержит `'use client'` и рендерит Web Component host; внутренний Shadow DOM создаётся после загрузки browser runtime. В Next.js App Router размещайте Viewer внутри отдельной Client Component boundary и используйте только на debug-маршруте или во внутреннем инструменте. - -## Generated-файлы, Git и CI - -Современный sprite-модуль создаёт локальный `.gitignore` для: - -```text -/.svg-sprite/ -``` - -Локальный `.gitignore` следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль: - -```json -{ - "scripts": { - "sprites": "svg-sprites src/ui/app-icons/svg-sprite.config.ts", - "predev": "npm run sprites", - "prebuild": "npm run sprites", - "pretypecheck": "npm run sprites" - } -} -``` - -CI должен устанавливать development dependencies и выполнять generation script до сборки или проверки типов. - -Если в каталоге спрайта уже находится пользовательский `.gitignore` либо пользовательский файл внутри `.svg-sprite`, генератор не перезапишет его. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API. - -## Диагностика - -- Нет `.svg-sprite/index.js`: запустите generation script до импорта generated-модуля. -- Не найден источник: передайте существующий config-файл или каталог sprite-модуля. -- Не указан mode: добавьте `mode` в config либо передайте `--mode`. -- Иконка отсутствует в типе: проверьте `inputFiles`, расширение `.svg` и уровень вложенности `inputFolder`. -- Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл. -- `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker. -- Иконка не меняет цвет: используйте `` или generated-компонент и проверьте `replaceColors`. -- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders. -- Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML. -- Viewer не видит спрайт: проверьте путь к `.svg-sprite/svg-sprite.manifest.js` и выполните генерацию до запуска приложения. -- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику. - -Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md). +Canonical документ: [технический справочник](reference/technical.md). diff --git a/docs/ru/reference/README.md b/docs/ru/reference/README.md new file mode 100644 index 0000000..e9d9b94 --- /dev/null +++ b/docs/ru/reference/README.md @@ -0,0 +1,4 @@ +# Справочники + +- [Технический справочник](technical.md) +- [Программный API](programmatic-api.md) diff --git a/docs/ru/reference/programmatic-api.md b/docs/ru/reference/programmatic-api.md new file mode 100644 index 0000000..209c76f --- /dev/null +++ b/docs/ru/reference/programmatic-api.md @@ -0,0 +1,147 @@ +# Программный API + +[Индекс документации](../README.md) + +Пакет распространяется как ESM и предоставляет единый Node.js API генерации. Framework-neutral Viewer находится в `@gromlab/svg-sprites/viewer`, auto-register entry — в `@gromlab/svg-sprites/viewer/element`, React bridge — в `@gromlab/svg-sprites/react`. + +## `generateSprite` + +```ts +import { generateSprite } from '@gromlab/svg-sprites' + +const result = await generateSprite( + 'src/ui/file-manager/svg-sprite/svg-sprite.config.ts', +) +``` + +Для static standalone mode `result.spritePath` можно использовать в build-скрипте, +чтобы опубликовать SVG по URL приложения: + +```ts +import { copyFile } from 'node:fs/promises' + +const result = await generateSprite('src/sprite/svg-sprite.config.ts', { + mode: 'standalone', +}) +await copyFile(result.spritePath, 'dist/app-icons/sprite.svg') +``` + +`spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON +manifest доступен через `result.manifestPath` и копируется независимо от SVG. + +Первый аргумент принимает полный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог. + +Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом: + +```ts +await generateSprite('src/ui/file-manager/svg-sprite/custom-config.json', { + mode: 'react@webpack', + name: 'documents', + input: ['./assets', '../../shared/search.svg'], + transform: { + addTransition: false, + }, + generatedNotice: false, +}) +``` + +Порядок разрешения настроек: + +```text +defaults → config → API overrides +``` + +Для полностью программной генерации передайте каталог и все обязательные настройки через overrides: + +```ts +await generateSprite('src/ui/file-manager/svg-sprite', { + mode: 'react@vite', + name: 'file-manager', + input: [ + '../../shared/search.svg', + '../../shared/settings.svg', + ], +}) +``` + +## Конфигурация + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'file-manager', + description: 'Иконки файлового менеджера', + input: ['./icons', '../../shared/check.svg'], + transform: { + removeSize: true, + replaceColors: true, + addTransition: true, + }, + generatedNotice: true, +}) +``` + +`input` принимает одну папку, SVG-файл или glob-паттерн либо массив, объединяющий такие источники. Если поле не задано, используется `./icons`; относительные пути считаются от папки с конфигом. + +`defineSpriteConfig` является identity helper для TypeScript autocomplete. JS может экспортировать тот же объект через `export default`, а JSON содержит объект непосредственно. + +## Специализированные обёртки + +Специализированные функции доступны как обёртки над `generateSprite`: + +```ts +import { generateNextSprite, generateReactSprite } from '@gromlab/svg-sprites' + +await generateReactSprite('path/to/config.ts', 'vite') +await generateNextSprite('path/to/config.ts', { + router: 'app', + bundler: 'turbopack', +}) +``` + +Явно переданный target перекрывает `mode` из файла. Для нового кода используйте `generateSprite`. + +## Config API + +```ts +import { + loadSpriteConfig, + resolveSpriteConfig, + validateSpriteConfig, +} from '@gromlab/svg-sprites' +``` + +- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл. +- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта. +- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`. + +## Низкоуровневый compiler + +```ts +import { + compileSprite, + compileSpriteContent, + createShapeTransform, +} from '@gromlab/svg-sprites' +``` + +Эти функции предназначены для собственного orchestration. Стандартная генерация должна выполняться через `generateSprite`. + +## Viewer runtime + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +``` + +Browser entry регистрирует ``. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler. + +React bridge сохраняет компонентный API: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +``` + +`SpriteViewer` принимает generated manifests, remote standalone sources, lazy loaders или результат `import.meta.glob`. React entry содержит `'use client'` и предназначен для debug-инструментов; production-компоненты импортируются из локальных sprite-модулей приложения. diff --git a/docs/ru/reference/technical.md b/docs/ru/reference/technical.md new file mode 100644 index 0000000..0122739 --- /dev/null +++ b/docs/ru/reference/technical.md @@ -0,0 +1,631 @@ +# Технический справочник + +[Индекс документации](../README.md) + +Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека: + +- [Bare standalone](../guides/standalone.md) +- [Standalone + Vite](../guides/standalone-vite.md) +- [Standalone + Webpack 5](../guides/standalone-webpack.md) +- [React + Vite](../guides/react-vite.md) +- [React + Webpack 5](../guides/react-webpack.md) +- [Next.js App Router + Turbopack](../guides/next-app-turbopack.md) +- [Next.js App Router + Webpack](../guides/next-app-webpack.md) +- [Next.js Pages Router + Turbopack](../guides/next-pages-turbopack.md) +- [Next.js Pages Router + Webpack](../guides/next-pages-webpack.md) + +## Требования + +- Node.js 18 или новее; +- пакет распространяется как ESM и подключается через `import`; +- React 18 или 19 требуется только для React/Next generated-компонентов и `@gromlab/svg-sprites/react`; +- для типизации package exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. + +Для генерации не нужна dependency проекта. Запускайте CLI через `npx`: + +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites path/to/svg-sprite.config.ts +``` + +Устанавливайте пакет как development dependency, только если проекту нужны +Viewer, типы конфига или программный API: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +## CLI и режимы генерации + +CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации: + +```text +svg-sprites [options] +``` + +| Среда | Mode | +|---|---| +| Static HTML / собственная публикация | `standalone` | +| Standalone + Vite | `standalone@vite` | +| Standalone + Webpack 5 | `standalone@webpack` | +| React + Vite | `react@vite` | +| React + Webpack 5 | `react@webpack` | +| Next.js App Router + Turbopack | `next@app/turbopack` | +| Next.js App Router + Webpack 5 | `next@app/webpack` | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | +| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | + +Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.ts`. + +Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`. + +Доступны `--mode`, `--name`, `--description`, повторяемый `--input `, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input` полностью заменяет значение `input` из config. + +В CLI заключайте glob-паттерны в одинарные кавычки, чтобы shell не раскрыл их до запуска генератора: + +```bash +svg-sprites --input './icons/**/*.svg' --input '!./icons/legacy/**' svg-sprite.config.ts +``` + +Mode должен соответствовать способу публикации приложения. Bare `standalone` оставляет публичный URL приложению; Vite и Webpack modes генерируют bundler-specific подключение SVG asset. + +## Единая конфигурация + +Каждый config-файл описывает один независимый спрайт. + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + name: 'app', + description: 'Общие иконки приложения', + input: [ + './local-icons', + '../../assets/icons/*.svg', + '!../../assets/icons/deprecated-*.svg', + ], + transform: { + removeSize: true, + replaceColors: true, + addTransition: true, + }, + generatedNotice: true, +}) +``` + +| Опция | Тип | По умолчанию | Назначение | +|---|---|---|---| +| `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API | +| `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов | +| `description` | `string` | Нет | Описание для типов и debug manifest | +| `input` | `string \| string[]` | `./icons` | Папки, SVG-файлы и glob-паттерны относительно папки конфига | +| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG | +| `generatedNotice` | `boolean` | `true` | Полное или короткое предупреждение в generated-файлах | + +### Имя спрайта + +`name` записывается в kebab-case и должно начинаться с латинской буквы: + +```text +app → AppIcon +file-manager → FileManagerIcon +``` + +Если `name` не задано, генератор выводит его из каталога. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога. + +### Источники иконок + +`SpriteConfig.input` является необязательным и имеет тип `string | string[]`. Если поле отсутствует, источником служит папка `./icons` относительно папки конфига. В config-less режиме относительные пути считаются от каталога, переданного CLI или API. + +Каждая строка без префикса `!` может быть путём к конкретной папке, конкретному файлу `.svg` или glob-паттерном. Папка включает только непосредственные дочерние `*.svg`. Для рекурсивного обхода вложенных каталогов укажите явный паттерн, например `icons/**/*.svg`. + +Массив объединяет все включающие источники. Паттерн с префиксом `!` глобально исключает совпадения из общего результата независимо от того, какой источник их добавил. + +Поддерживается следующий glob-синтаксис: + +| Синтаксис | Значение | +|---|---| +| `*` | Любые символы внутри одного сегмента пути | +| `**` | Любое число вложенных каталогов | +| `?` | Один символ внутри сегмента пути | +| `{a,b}` | Одна из альтернатив | +| `[abc]` | Один символ из набора или диапазона | +| `!pattern` | Исключение совпадений из всего объединённого input | + +Каждый включающий источник или паттерн должен найти хотя бы один SVG, иначе генерация завершается ошибкой. Повторяющиеся пути удаляются, а итоговый список файлов детерминированно сортируется. Разные SVG с одинаковым basename по-прежнему считаются конфликтом, потому что basename задаёт публичное имя иконки. + +## Generated-модуль + +После генерации React- или Next.js-каталог спрайта выглядит так: + +```text +app-icons/ +├── .gitignore +├── svg-sprite.config.ts +├── index.ts # необязательный пользовательский barrel +└── .svg-sprite/ + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts + ├── sprite.svg + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css +``` + +| Файл | Назначение | +|---|---| +| `.svg-sprite/index.js` | Mode-specific production facade и runtime-список имён | +| `.svg-sprite/index.d.ts` | Публичные декларации facade, компонента и union-типа имён | +| `.svg-sprite/svg-sprite.manifest.js` | Debug metadata и URL asset для `SpriteViewer` | +| `.svg-sprite/sprite.svg` | Собранный SVG-спрайт | +| `.svg-sprite/react/react-component.js` | Runtime React-компонента без TypeScript и JSX | +| `.svg-sprite/react/react-component.d.ts` | Props, style и declaration React-компонента | +| `.svg-sprite/react/react-component.module.css` | Стили конкретной React-реализации | +| `.svg-sprite/icon-data.js` | Runtime-список имён и внутренние IDs | +| `.svg-sprite/*.d.ts` | TypeScript-декларации соответствующих JS-модулей | + +Standalone-контракты не создают каталог `react/`. Bare `standalone` содержит только +runtime asset и deployment-neutral manifest data: + +```text +.svg-sprite/ +├── sprite.svg +└── svg-sprite.manifest.json +``` + +`standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, +`icon-data.*` и resolved `svg-sprite.manifest.*`. Их facade содержит нативный +generated Web Component без внешних runtime-зависимостей. Bare `standalone` +намеренно не создаёт JavaScript-компонент. + +Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой `index.ts` генератору не принадлежит; при необходимости создайте пользовательский barrel: + +```ts +export * from './.svg-sprite' +``` + +## Standalone Web Component и TypeScript + +В modes `standalone@vite` и `standalone@webpack` спрайт с `name: 'app'` +экспортирует функцию регистрации `defineAppIconElement()` и tag ``: + +```ts +import { defineAppIconElement } from '@/ui/app-icons' + +defineAppIconElement() +``` + +После регистрации элемент можно использовать в HTML: + +```html + + + +``` + +Компонент рендерит `` в открытом Shadow DOM, сам выбирает внутренний +ID и `viewBox`, а URL asset получает через соответствующий Vite или Webpack +механизм. Размер host по умолчанию равен `1em × 1em`; `class`, `style`, `color` +и `--icon-color-N` задаются обычным CSS. + +Generated `HTMLElementTagNameMap` типизирует property API: + +```ts +const icon = document.createElement('app-icon') + +icon.icon = 'search' +icon.icon = 'unknown' // ошибка TypeScript +``` + +Значения атрибутов в обычной HTML-разметке TypeScript не проверяет. Поэтому +неизвестный `icon="unknown"` дополнительно проверяется в runtime: компонент +скрывает внутренний SVG и сообщает об ошибке, не создавая fragment +`#undefined`. Повторный вызов `defineAppIconElement()` безопасен для того же +спрайта; конфликт с другим элементом под tag `` завершается ошибкой. + +## React-компонент и TypeScript + +Спрайт с `name: 'app'` экспортирует: + +```ts +export { AppIcon, appIconNames } +export type { AppIconName, AppIconProps, AppIconStyle } +``` + +### Имена иконок + +Имена SVG-файлов становятся допустимыми значениями `icon`: + +```tsx + + // ошибка TypeScript +``` + +Runtime-список содержит те же значения: + +```ts +import { appIconNames } from '@/ui/app-icons' + +// readonly ['search', 'settings', 'user'] +``` + +Имена с пробелами и другими небезопасными для SVG ID символами остаются частью публичного API. Для внутреннего fragment ID генератор создаёт стабильный безопасный hash: + +```text +folder open.svg → icon="folder open" → id="icon-" +``` + +Для таких имён используйте generated-компонент или `id` из debug manifest, а не формируйте fragment ID вручную. + +### SVG-атрибуты + +По умолчанию компонент рендерит `` и принимает стандартные SVG-атрибуты: + +```tsx + +``` + +Компонент не добавляет accessibility-семантику автоматически. Передавайте подходящие `aria-*`, `role` или подпись в зависимости от назначения иконки. + +### Обёртка + +`wrapped` рендерит `` с внутренним SVG. Остальные props в этом режиме относятся к ``: + +```tsx + +``` + +### Типизированные CSS-переменные + +`AppIconStyle` расширяет `CSSProperties` и поддерживает свойства вида `--icon-color-N`: + +```tsx + +``` + +## Множественные спрайты + +Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset: + +```text +app-icons → AppIcon → общие иконки +analytics-icons → AnalyticsIcon → иконки страницы аналитики +editor-icons → EditorIcon → иконки редактора +``` + +Один исходный SVG можно добавить через `input` в несколько конфигураций. Копировать файл в каталоги каждого спрайта не требуется. + +Для нескольких спрайтов добавьте отдельную CLI-команду для каждого каталога или объедините команды в общем npm script. + +## Форматы и способы отображения + +Все текущие modes создают формат `stack`. + +| Формат | `` | `` | CSS background | +|---|---:|---:|---:| +| `stack` | Да | Да | Да | + +### Generated-компонент + +Для React и Next.js используйте generated React-компонент. Он знает внутренние ID, формирует URL и предоставляет TypeScript API: + +```tsx + +``` + +Для `standalone@vite` и `standalone@webpack` используйте generated Web Component: + +```html + +``` + +### Вручную через `` + +Способ получения `spriteUrl` зависит от сборщика. + +Static HTML после публикации `.svg-sprite/sprite.svg` приложением: + +```html + +``` + +Standalone Vite/Webpack предоставляет generated `getIconsIconHref()` и mapping +внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную. + +Vite: + +```ts +import spriteUrl from './.svg-sprite/sprite.svg?no-inline' +``` + +Webpack 5, Turbopack и Next.js: + +```ts +const spriteUrl = new URL('./.svg-sprite/sprite.svg', import.meta.url).href +``` + +После получения URL используйте его в JSX: + +```tsx + + + +``` + +Для имён, небезопасных как SVG ID, используйте внутренний `id` из manifest. + +### Через `` + +```tsx +Поиск +``` + +SVG внутри `` изолирован от CSS страницы. `color` и `--icon-color-N` на внешнем элементе не изменяют его внутренние цвета. + +### Через CSS + +```css +.icon { + background: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; +} +``` + +Для одноцветного силуэта можно использовать mask: + +```css +.icon { + background-color: currentColor; + mask: url('./.svg-sprite/sprite.svg#search') center / contain no-repeat; +} +``` + +Mask не сохраняет исходные цвета, gradients и различия между `fill` и `stroke`. + +Путь в CSS разрешается относительно самого CSS-файла. В примерах CSS-файл находится рядом с `svg-sprite.config.ts`. + +## Assets и кеширование + +Generated component или standalone facade передаёт SVG сборщику как отдельный asset: + +- Vite использует статический импорт с `?no-inline`; +- Webpack 5, Turbopack и Next.js используют `new URL(..., import.meta.url)`; +- SVG path-данные не сериализуются в generated JavaScript. + +Bare `standalone` не участвует в asset pipeline: приложение само копирует или +публикует `sprite.svg` и отвечает за URL, версионирование и cache policy. + +При стандартном именовании assets сборщик добавляет content hash: + +```text +/assets/sprite-.svg +``` + +Это позволяет кешировать SVG отдельно от JavaScript. Изменение React-кода не меняет содержимое спрайта, а изменение иконок создаёт новую версию asset. + +HTTP cache headers, CDN и `Cache-Control` настраиваются приложением или платформой размещения. Для Webpack имя итогового файла зависит от `assetModuleFilename` проекта. + +## Трансформации SVG + +Все трансформации включены по умолчанию и настраиваются независимо: + +| Опция | Что делает | +|---|---| +| `removeSize` | Удаляет `width` и `height` с корневого ``, сохраняя существующий `viewBox` | +| `replaceColors` | Заменяет найденные `fill` и `stroke` на `--icon-color-N` | +| `addTransition` | Добавляет transitions для `fill` и `stroke` в цветные элементы и generated styles | + +Чтобы отключить отдельную операцию: + +```ts +export default defineSpriteConfig({ + mode: 'next@app/turbopack', + transform: { + removeSize: false, + replaceColors: false, + addTransition: false, + }, +}) +``` + +Исходные SVG не изменяются. Трансформации применяются только к содержимому generated-спрайта. + +## Управление цветами + +### Монохромные иконки + +Если найден один цвет, fallback становится `currentColor`: + +```svg +stroke="var(--icon-color-1, currentColor)" +``` + +Цвет задаётся через prop или CSS: + +```tsx + +``` + +### Многоцветные иконки + +Каждый уникальный цвет получает отдельную переменную с исходным fallback: + +```svg +fill="var(--icon-color-1, #798198)" +fill="var(--icon-color-2, #ffffff)" +fill="var(--icon-color-3, #129d9d)" +``` + +Можно заменить только необходимые значения: + +```css +.icon { + --icon-color-1: #4b5563; + --icon-color-3: #14b8a6; +} +``` + +### Ограничения + +- `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются; +- надёжнее всего обрабатываются цвета в атрибутах `fill`, `stroke` и inline `style`; +- CSS-классы и внешние stylesheets внутри SVG не являются основным сценарием трансформации; +- значения `url(#...)` могут быть заменены вместе с цветами, поэтому gradients и patterns требуют отдельного спрайта с `replaceColors: false`; +- masks, filters и сложные внутренние CSS-правила требуют визуальной проверки; +- CSS-переменные страницы доступны через ``, но не внутри `` и CSS background. + +Для сложной иконки можно отключить `replaceColors` в конфигурации отдельного спрайта. + +## SpriteViewer + +Viewer использует один Web Component с Shadow DOM для всех modes. React и будущие framework-компоненты являются bridge к этому же элементу, поэтому визуал и поведение не дублируются. + +Bare `standalone` подключает самостоятельный browser bundle и передаёт URL JSON manifest и опубликованного SVG: + +```html + + + +``` + +`viewer-element.js` не имеет дополнительных runtime-файлов и может быть скопирован с остальными static assets для self-hosting. + +`standalone@vite` и `standalone@webpack` регистрируют тот же элемент через npm entry и передают generated JS manifest через свойство `sources`: + +```ts +import '@gromlab/svg-sprites/viewer/element' +import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' +import spriteManifest from './svg-sprite/.svg-sprite/svg-sprite.manifest.js' + +const viewer = document.querySelector('gromlab-sprite-viewer')! +viewer.sources = [spriteManifest] +``` + +React и Next.js сохраняют компонентный API: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +``` + +Он принимает готовые manifests, remote standalone sources, массив lazy loaders или record формата `import.meta.glob`. + +Vite: + +```tsx +import { SpriteViewer } from '@gromlab/svg-sprites/react' +import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' + +const sources = import.meta.glob( + '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', +) + +export const IconsDebugPage = () => ( + +) +``` + +Webpack и Next.js: + +```tsx +const sources = [ + () => import('@/ui/app-icons/.svg-sprite/svg-sprite.manifest.js'), + () => import('@/features/analytics/icons/.svg-sprite/svg-sprite.manifest.js'), +] + +export const IconsDebugPage = () => ( + +) +``` + +Viewer показывает группы, поиск, `viewBox`, CSS-переменные и fallback-цвета. React/Next manifests получают вкладки React, SVG, IMG и CSS; standalone manifests получают SVG, IMG и CSS. Цветовые значения можно менять в интерфейсе и сразу проверять результат. + +### Тема Viewer + +По умолчанию `colorTheme="auto"` следует `prefers-color-scheme`. Можно передать `light` или `dark` явно: + +```tsx + +``` + +Для синхронизации с темой приложения: + +```tsx + +``` + +`@gromlab/svg-sprites/react` содержит `'use client'` и рендерит Web Component host; внутренний Shadow DOM создаётся после загрузки browser runtime. В Next.js App Router размещайте Viewer внутри отдельной Client Component boundary и используйте только на debug-маршруте или во внутреннем инструменте. + +## Generated-файлы, Git и CI + +Все modes, кроме bare `standalone`, создают локальный `.gitignore` для: + +```text +/.svg-sprite/ +``` + +Локальный `.gitignore` следует один раз добавить в репозиторий. Он исключает остальные generated-файлы, поэтому генерацию нужно запускать перед командами, которые импортируют sprite-модуль: + +```json +{ + "scripts": { + "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "predev": "npm run sprites", + "prebuild": "npm run sprites", + "pretypecheck": "npm run sprites" + } +} +``` + +CI должен выполнять generation script до сборки или проверки типов. Для воспроизводимости замените `latest` на точную версию. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API. + +Bare `standalone` не создаёт и не изменяет `.gitignore`: приложение само решает, коммитить или игнорировать его `.svg-sprite/`. В остальных modes генератор не перезапишет пользовательский `.gitignore`. Он также откажется перезаписывать пользовательский файл внутри `.svg-sprite`. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API. + +## Диагностика + +- Нет `.svg-sprite/index.js`: запустите generation script до импорта generated-модуля. +- Не найден источник: передайте существующий config-файл или каталог sprite-модуля. +- Не указан mode: добавьте `mode` в config либо передайте `--mode`. +- Иконка отсутствует в типе: проверьте `input`, расширение `.svg`, glob-исключения и необходимость `**/*.svg` для вложенных папок. +- Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл. +- `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker. +- Иконка не меняет цвет: используйте `` или generated-компонент и проверьте `replaceColors`. +- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders. +- Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML. +- Viewer не видит спрайт: проверьте путь к `.svg-sprite/svg-sprite.manifest.js` и выполните генерацию до запуска приложения. +- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику. + +Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md). diff --git a/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts b/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts index 985b455..bbcc2c5 100644 --- a/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/next-app-turbopack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'next@app/turbopack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts index 35850ae..8baf3fd 100644 --- a/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/next-app-webpack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'next@app/webpack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts b/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts index c26c99d..30b8867 100644 --- a/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/next-pages-turbopack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'next@pages/turbopack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts index f059336..c9eb5b8 100644 --- a/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/next-pages-webpack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'next@pages/webpack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/react-vite/src/sprite/svg-sprite.config.ts b/integration/apps/react-vite/src/sprite/svg-sprite.config.ts index fb603c4..8b5d43e 100644 --- a/integration/apps/react-vite/src/sprite/svg-sprite.config.ts +++ b/integration/apps/react-vite/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'react@vite', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts index 9043d84..18e52eb 100644 --- a/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/react-webpack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'react@webpack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/standalone-vite/src/generated-contract.typecheck.ts b/integration/apps/standalone-vite/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..0a988fa --- /dev/null +++ b/integration/apps/standalone-vite/src/generated-contract.typecheck.ts @@ -0,0 +1,9 @@ +import type { IconsIconElement } from './sprite' + +const icon = document.createElement('icons-icon') +const typedIcon: IconsIconElement = icon + +typedIcon.icon = 'check' + +// @ts-expect-error Generated icon names form a literal union. +typedIcon.icon = 'missing' diff --git a/integration/apps/standalone-vite/src/main.ts b/integration/apps/standalone-vite/src/main.ts index ead284d..366ca2e 100644 --- a/integration/apps/standalone-vite/src/main.ts +++ b/integration/apps/standalone-vite/src/main.ts @@ -1,7 +1,7 @@ import '@gromlab/svg-sprites/viewer/element' import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' -import { getIconsIconHref, iconsSpriteUrl } from './sprite' +import { defineIconsIconElement, iconsSpriteUrl } from './sprite' import './style.css' const check = spriteManifest.icons.find((icon) => icon.name === 'check') @@ -9,16 +9,17 @@ if (!check || spriteManifest.spriteUrl !== iconsSpriteUrl) { throw new Error('Generated Vite facade and manifest disagree.') } +defineIconsIconElement() + document.querySelector('#app')!.innerHTML = `

Standalone + Vite

- - - + >
` diff --git a/integration/apps/standalone-vite/src/sprite/index.ts b/integration/apps/standalone-vite/src/sprite/index.ts index 659338f..b9c7a54 100644 --- a/integration/apps/standalone-vite/src/sprite/index.ts +++ b/integration/apps/standalone-vite/src/sprite/index.ts @@ -1,7 +1,9 @@ export { + defineIconsIconElement, getIconsIconHref, iconsIconIds, iconsIconNames, + iconsIconTagName, iconsSpriteUrl, } from './.svg-sprite/index.js' -export type { IconsIconName } from './.svg-sprite/index.js' +export type { IconsIconElement, IconsIconName } from './.svg-sprite/index.js' diff --git a/integration/apps/standalone-vite/src/sprite/svg-sprite.config.ts b/integration/apps/standalone-vite/src/sprite/svg-sprite.config.ts index 1ffd01d..4e2b016 100644 --- a/integration/apps/standalone-vite/src/sprite/svg-sprite.config.ts +++ b/integration/apps/standalone-vite/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'standalone@vite', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/standalone-webpack/src/generated-contract.typecheck.ts b/integration/apps/standalone-webpack/src/generated-contract.typecheck.ts new file mode 100644 index 0000000..0a988fa --- /dev/null +++ b/integration/apps/standalone-webpack/src/generated-contract.typecheck.ts @@ -0,0 +1,9 @@ +import type { IconsIconElement } from './sprite' + +const icon = document.createElement('icons-icon') +const typedIcon: IconsIconElement = icon + +typedIcon.icon = 'check' + +// @ts-expect-error Generated icon names form a literal union. +typedIcon.icon = 'missing' diff --git a/integration/apps/standalone-webpack/src/main.ts b/integration/apps/standalone-webpack/src/main.ts index a8063c8..8298bf2 100644 --- a/integration/apps/standalone-webpack/src/main.ts +++ b/integration/apps/standalone-webpack/src/main.ts @@ -1,24 +1,25 @@ import '@gromlab/svg-sprites/viewer/element' import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' -import { getIconsIconHref, iconsSpriteUrl } from './sprite' +import { defineIconsIconElement, iconsSpriteUrl } from './sprite' const check = spriteManifest.icons.find((icon) => icon.name === 'check') if (!check || spriteManifest.spriteUrl !== iconsSpriteUrl) { throw new Error('Generated Webpack facade and manifest disagree.') } +defineIconsIconElement() + document.querySelector('#app')!.innerHTML = `

Standalone + Webpack

- - - + >
` diff --git a/integration/apps/standalone-webpack/src/sprite/index.ts b/integration/apps/standalone-webpack/src/sprite/index.ts index 659338f..b9c7a54 100644 --- a/integration/apps/standalone-webpack/src/sprite/index.ts +++ b/integration/apps/standalone-webpack/src/sprite/index.ts @@ -1,7 +1,9 @@ export { + defineIconsIconElement, getIconsIconHref, iconsIconIds, iconsIconNames, + iconsIconTagName, iconsSpriteUrl, } from './.svg-sprite/index.js' -export type { IconsIconName } from './.svg-sprite/index.js' +export type { IconsIconElement, IconsIconName } from './.svg-sprite/index.js' diff --git a/integration/apps/standalone-webpack/src/sprite/svg-sprite.config.ts b/integration/apps/standalone-webpack/src/sprite/svg-sprite.config.ts index 6d4be55..0d1c666 100644 --- a/integration/apps/standalone-webpack/src/sprite/svg-sprite.config.ts +++ b/integration/apps/standalone-webpack/src/sprite/svg-sprite.config.ts @@ -3,6 +3,6 @@ import { defineSpriteConfig } from '@gromlab/svg-sprites' export default defineSpriteConfig({ mode: 'standalone@webpack', name: 'icons', - inputFiles: ['../../../../fixtures/icons/check.svg'], + input: '../../../../fixtures/icons/check.svg', generatedNotice: false, }) diff --git a/integration/apps/standalone/src/sprite/.gitignore b/integration/apps/standalone/src/sprite/.gitignore deleted file mode 100644 index 7f3344e..0000000 --- a/integration/apps/standalone/src/sprite/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# @generated by @gromlab/svg-sprites. Do not edit. -/.svg-sprite/ diff --git a/integration/apps/standalone/src/sprite/svg-sprite.config.json b/integration/apps/standalone/src/sprite/svg-sprite.config.json index 8c51c85..2fedb7e 100644 --- a/integration/apps/standalone/src/sprite/svg-sprite.config.json +++ b/integration/apps/standalone/src/sprite/svg-sprite.config.json @@ -1,6 +1,6 @@ { "mode": "standalone", "name": "app-icons", - "inputFiles": ["../../../../fixtures/icons/check.svg"], + "input": "../../../../fixtures/icons/check.svg", "generatedNotice": false } diff --git a/integration/package-lock.json b/integration/package-lock.json index 8514732..52290d2 100644 --- a/integration/package-lock.json +++ b/integration/package-lock.json @@ -23,7 +23,8 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", - "svg-sprite": "^2.0.4" + "svg-sprite": "^2.0.4", + "tinyglobby": "^0.2.17" }, "bin": { "svg-sprites": "dist/cli.js" diff --git a/integration/tests/apps.spec.ts b/integration/tests/apps.spec.ts index 3a4de4e..ee4ea9b 100644 --- a/integration/tests/apps.spec.ts +++ b/integration/tests/apps.spec.ts @@ -111,6 +111,12 @@ for (const app of apps) { await expect(icon).toBeVisible() await expect(icon).toHaveAttribute('data-app', app.id) + if (app.id === 'standalone-vite' || app.id === 'standalone-webpack') { + expect(await icon.evaluate((element) => element.tagName.toLowerCase())).toBe('icons-icon') + expect(await icon.evaluate((element) => element.shadowRoot !== null)).toBe(true) + await expect(icon.locator('svg')).toHaveAttribute('viewBox', '0 0 24 24') + } + const href = await icon.locator('use').getAttribute('href') expect(href).toBeTruthy() expect(href).not.toMatch(/^(?:blob|data|file):/) diff --git a/package-lock.json b/package-lock.json index d6411fa..1f1f65f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", - "svg-sprite": "^2.0.4" + "svg-sprite": "^2.0.4", + "tinyglobby": "^0.2.17" }, "bin": { "svg-sprites": "dist/cli.js" @@ -1895,7 +1896,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -2443,7 +2443,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2916,10 +2915,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", diff --git a/package.json b/package.json index 1056b34..08a61c5 100644 --- a/package.json +++ b/package.json @@ -49,8 +49,8 @@ "dist/chunk-*.js", "dist/chunk-*.js.map", "README_RU.md", - "docs/en/*.md", - "docs/ru/*.md", + "docs/en", + "docs/ru", "LICENSE", "THIRD_PARTY_NOTICES.md" ], @@ -97,7 +97,8 @@ "dependencies": { "colorette": "^2.0.20", "jiti": "^2.6.1", - "svg-sprite": "^2.0.4" + "svg-sprite": "^2.0.4", + "tinyglobby": "^0.2.17" }, "peerDependencies": { "react": ">=18 <20" diff --git a/skills/README.md b/skills/README.md index 5c7c978..eaefff2 100644 --- a/skills/README.md +++ b/skills/README.md @@ -1,6 +1,6 @@ # AI skills -Исходники английского и русского скиллов находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. +Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Канонические exact-mode guides находятся в `docs/{en,ru}/guides/` и копируются в соответствующий skill без изменения. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. Обе языковые версии имеют одинаковую структуру: @@ -17,15 +17,11 @@ src// │ ├── 60-verification.md │ └── 70-diagnostics.md └── references/ - ├── react-vite.md - ├── react-webpack.md - ├── next-app.md - ├── next-pages.md ├── programmatic-api.md └── complex-svg.md ``` -`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. `references/` содержит самостоятельные инструкции для агента по конкретным стекам и редким сценариям. Пользовательские `README*.md` и `docs/{en,ru}/*.md` дополнительно копируются в `references/upstream/` как вторичный источник полного публичного API. +`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. Локальный `references/` содержит только agent-specific материалы. Девять файлов из `docs//guides/` копируются в `references/guides/`; английский artifact получает только английские guides, русский только русские. Второй набор mode guides и каталог `references/upstream/` не создаются. ## Композиция Markdown diff --git a/skills/svg-sprites/skill.config.mjs b/skills/svg-sprites/skill.config.mjs index 5c0002e..642c338 100644 --- a/skills/svg-sprites/skill.config.mjs +++ b/skills/svg-sprites/skill.config.mjs @@ -1,12 +1,20 @@ const agentReferences = [ - 'react-vite.md', - 'react-webpack.md', - 'next-app.md', - 'next-pages.md', 'programmatic-api.md', 'complex-svg.md', ] +const guideFiles = [ + 'standalone.md', + 'standalone-vite.md', + 'standalone-webpack.md', + 'react-vite.md', + 'react-webpack.md', + 'next-app-turbopack.md', + 'next-app-webpack.md', + 'next-pages-turbopack.md', + 'next-pages-webpack.md', +] + function documents(language) { return [ { entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true }, @@ -17,36 +25,28 @@ function documents(language) { ] } -const upstream = [ - { from: '../../README.md', to: 'references/upstream/README.md' }, - { from: '../../README_RU.md', to: 'references/upstream/README_RU.md' }, - { - fromDirectory: '../../docs/en', - toDirectory: 'references/upstream/docs/en', - extensions: ['.md'], - }, - { - fromDirectory: '../../docs/ru', - toDirectory: 'references/upstream/docs/ru', - extensions: ['.md'], - }, -] +function guides(language) { + return guideFiles.map((file) => ({ + from: `../../docs/${language}/guides/${file}`, + to: `references/guides/${file}`, + })) +} export default [ { name: 'svg-sprites', - description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', + description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', output: '../artifacts/svg-sprites', maxSkillBytes: 48_000, documents: documents('en'), - copy: upstream, + copy: guides('en'), }, { name: 'svg-sprites-ru', - description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', + description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', output: '../artifacts/svg-sprites-ru', maxSkillBytes: 48_000, documents: documents('ru'), - copy: upstream, + copy: guides('ru'), }, ] diff --git a/skills/svg-sprites/src/en/SKILL.md b/skills/svg-sprites/src/en/SKILL.md index 6211008..ed0909b 100644 --- a/skills/svg-sprites/src/en/SKILL.md +++ b/skills/svg-sprites/src/en/SKILL.md @@ -11,8 +11,9 @@ ## References to open as needed -- For React + Vite, open [React + Vite](./references/react-vite.md); for React with a custom Webpack 5 setup, open [React + Webpack](./references/react-webpack.md). -- For Next.js, open the guide for the [App Router](./references/next-app.md) or [Pages Router](./references/next-pages.md), then select the section for the bundler actually in use. +- For static publishing, open [bare standalone](./references/guides/standalone.md). For vanilla bundler apps, open [standalone + Vite](./references/guides/standalone-vite.md) or [standalone + Webpack](./references/guides/standalone-webpack.md). +- For React, open the exact [Vite](./references/guides/react-vite.md) or [Webpack](./references/guides/react-webpack.md) guide. +- For the Next.js App Router, open the exact [Turbopack](./references/guides/next-app-turbopack.md) or [Webpack](./references/guides/next-app-webpack.md) guide. +- For the Next.js Pages Router, open the exact [Turbopack](./references/guides/next-pages-turbopack.md) or [Webpack](./references/guides/next-pages-webpack.md) guide. - To invoke the generator from Node.js, open the [programmatic API](./references/programmatic-api.md). - For gradients, filters, `url(#...)`, unusual colors, and `viewBox` issues, open [complex SVGs](./references/complex-svg.md). -- Use the complete user documentation as a secondary source: [package README](./references/upstream/README.md). diff --git a/skills/svg-sprites/src/en/core/00-package-overview.md b/skills/svg-sprites/src/en/core/00-package-overview.md index d10fc99..c4ebb9d 100644 --- a/skills/svg-sprites/src/en/core/00-package-overview.md +++ b/skills/svg-sprites/src/en/core/00-package-overview.md @@ -1,15 +1,16 @@ ## What the package does -`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external sprite asset, supports standalone projects, and creates typed components for React/Next.js. +`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external sprite asset, creates a native typed Web Component for standalone bundler modes, and creates a React component for React/Next.js. The package supports multiple independent sprites in one project. Each explicitly selected config file or config-less directory describes one sprite and gets its own: - SVG asset; - mode-specific manifest data; - icon name types and `.svg-sprite/index.js` for bundler modes; +- a native Web Component with an explicit registration function for `standalone@vite`/`standalone@webpack`; - a React component only for React/Next.js; - a deployment-neutral JSON manifest without a public URL for bare `standalone`. The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. The names `FileManagerIcon` and `fileManagerIconNames` used below are examples of the API for one possible sprite, not fixed package exports. -Generated production runtime does not import `@gromlab/svg-sprites` at runtime. Install the package as a development dependency so configuration helpers and the local CLI use the version recorded in the project's lockfile. +Generated production runtime and declarations do not import `@gromlab/svg-sprites`. Generation can run through a pinned `npx --package` command without adding the package to the project. Install it as a development dependency only for the Viewer, package-provided config types, or the programmatic API. diff --git a/skills/svg-sprites/src/en/core/10-mode-selection.md b/skills/svg-sprites/src/en/core/10-mode-selection.md index 52c8e72..206fd8c 100644 --- a/skills/svg-sprites/src/en/core/10-mode-selection.md +++ b/skills/svg-sprites/src/en/core/10-mode-selection.md @@ -21,10 +21,10 @@ The CLI accepts exactly one path. A `.ts`, `.js`, or `.json` file loads that exa ```json { "scripts": { - "sprite:": "svg-sprites ", - "sprite::cli": "svg-sprites --mode " + "sprite:": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites ", + "sprite::cli": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --mode " } } ``` -Do not use incomplete `react`, `next@app`, `next@pages`, or `standalone@` keys, or the removed `legacy` mode. Use bare `standalone` only when the application publishes the SVG itself; use the complete Vite/Webpack key otherwise. Create one command per config file or directory when the project has multiple sprites. +Generation through `npx` does not add the package to the project. Pin an exact package version instead of `latest` in CI. Do not use incomplete `react`, `next@app`, `next@pages`, or `standalone@` keys, or the removed `legacy` mode. Use bare `standalone` only when the application publishes the SVG itself; use the complete Vite/Webpack key otherwise. Create one command per config file or directory when the project has multiple sprites. diff --git a/skills/svg-sprites/src/en/core/20-project-inspection.md b/skills/svg-sprites/src/en/core/20-project-inspection.md index 8ae0c1a..800f700 100644 --- a/skills/svg-sprites/src/en/core/20-project-inspection.md +++ b/skills/svg-sprites/src/en/core/20-project-inspection.md @@ -9,14 +9,14 @@ Establish the project's actual contract before making changes: 5. For a new sprite, choose a target directory without imposing a particular application layer or architecture. 6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`. -All config paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory: +All input paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory. Inspect `input` using this single contract: -- `inputFolder` defaults to `./icons`; -- `inputFiles` contains additional relative paths to individual SVGs and is merged with `inputFolder`; -- the same absolute file is deduplicated, but different files with the same icon basename cause an error; -- scanning `inputFolder` is shallow: only immediate files ending in `.svg` are read, and nested directories are not traversed; -- an explicitly configured `inputFolder` that does not exist is an error; -- if `inputFolder` is omitted, `./icons` does not exist, and `inputFiles` is non-empty, generation uses only `inputFiles`; -- an empty final input set, a missing file, or a path that does not point to an `.svg` file is an error. +- `input?: string | string[]` defaults to `./icons`; +- each string is a folder, an exact SVG file, or a glob; +- a folder is scanned shallowly; nested files are included only by an explicit recursive glob such as `./icons/**/*.svg`; +- an array combines positive sources, while an item prefixed with `!` excludes its matches from the combined set; +- every positive source must resolve to at least one SVG, so a missing or empty folder, an unmatched glob, a missing file, or a non-SVG exact file is an error; +- resolved files are deduplicated and sorted deterministically; +- different files with the same basename are a conflict, even when they came from different sources. -Do not copy a shared SVG into several folders: add its relative path to `inputFiles` in every sprite that needs it. If a recursive structure is required, list the files explicitly or reorganize the sources; the generator does not perform recursive scanning. +Do not copy a shared SVG into several folders: add its exact path or a suitable glob to `input` in every sprite that needs it. Use `**/*.svg` only when recursive inclusion is intentional. diff --git a/skills/svg-sprites/src/en/core/30-react-next-setup.md b/skills/svg-sprites/src/en/core/30-react-next-setup.md index 57d9ecd..4a2c363 100644 --- a/skills/svg-sprites/src/en/core/30-react-next-setup.md +++ b/skills/svg-sprites/src/en/core/30-react-next-setup.md @@ -12,27 +12,21 @@ src/ui/file-manager/svg-sprite/ One `svg-sprite.config.ts` creates one independent sprite. For multiple sets, choose multiple directories and assign each a unique `name`. -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Use the configuration helper for autocomplete and type checking: +The generator does not need to be installed in the project. Start with a plain +config that has no package import: ```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ +export default { mode: 'react@vite', name: 'file-manager', description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../shared/icons/close.svg'], -}) + input: ['./icons', '../../shared/icons/close.svg'], +} ``` -The object contract is the same for React and Next.js; only the full `mode` differs. +`input` accepts one folder, exact SVG file, or glob, or an array that combines them. Prefix an array item with `!` to exclude matches. Folders are shallow; use an explicit `**/*.svg` glob for recursion. Omit `input` to use `./icons`. Every path is resolved from the config directory, and every positive source must match at least one SVG. + +The object contract is the same for React and Next.js; only the full `mode` differs. Install the package only for the optional Viewer, programmatic API, or package-provided config typing. Exact guides also provide a local copy-paste config type for projects that remain package-free. `name` must begin with an ASCII letter and use kebab-case. The example `file-manager` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`. Another sprite gets its own names. If `name` is omitted, the generator derives it from the directory. @@ -41,7 +35,7 @@ Add a separate command with the selected mode key and one path: ```json { "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", + "sprite:file-manager": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "sprites": "npm run sprite:file-manager" } } @@ -49,9 +43,18 @@ Add a separate command with the selected mode key and one path: For Next.js, set the full key in the config, for example `next@app/turbopack`. For multiple sprites, add one `sprite:` command per config file and invoke them sequentially from `sprites`. -Generated files in `.svg-sprite` are excluded from Git by default, so run `sprites` before any process that needs the component, types, or asset. If the project imports the sprite-module root, create a user-owned `index.ts` with `export * from './.svg-sprite'`. Add generation to `predev`, `prebuild`, and, when a `typecheck` script exists, `pretypecheck`. +To select sources from the CLI instead, repeat `--input `; the values form the same array contract, including `!` exclusions: -If a lifecycle script is missing, create it. If it already exists, preserve its command and append generation with `&&`; for example, change `"prebuild": "npm run lint"` to `"prebuild": "npm run lint && npm run sprites"`. Never replace an existing `pre*` script with generation alone, and never create a duplicate JSON key. +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts \ + --input ./icons \ + --input '../../shared/icons/**/*.svg' \ + --input '!../../shared/icons/legacy-*.svg' +``` + +Generated files in `.svg-sprite` are excluded from Git by default, so run `sprites` before any process that needs the component, types, or asset. If the project imports the sprite-module root, create a user-owned `index.ts` with `export * from './.svg-sprite'`. Generated declarations are self-contained and do not import the generator package. + +Run generation either from `predev`/`prebuild`/`pretypecheck` hooks or explicitly inside the corresponding commands. Do not use both forms for the same command, or generation runs twice. Preserve existing script commands and never create a duplicate JSON key. Run the first generation manually: diff --git a/skills/svg-sprites/src/en/core/40-generated-contract.md b/skills/svg-sprites/src/en/core/40-generated-contract.md index 178d31f..136c737 100644 --- a/skills/svg-sprites/src/en/core/40-generated-contract.md +++ b/skills/svg-sprites/src/en/core/40-generated-contract.md @@ -9,7 +9,6 @@ svg-sprite/ ├── index.ts # optional user-owned barrel ├── .gitignore # managed by the generator └── .svg-sprite/ - ├── state.json # ownership registry and contract version ├── index.js ├── index.d.ts ├── icon-data.js @@ -25,23 +24,25 @@ svg-sprite/ Standalone does not create `react/`. Bare `standalone` generates `sprite.svg` and `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` additionally -generate `index.*`, `icon-data.*`, and a resolved manifest. +generate `index.*`, `icon-data.*`, and a resolved manifest. Their `index.*` also +contains a native generated Web Component; bare `standalone` gets no JavaScript runtime +and does not create or modify `.gitignore`. -Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change `.gitignore` or anything in `.svg-sprite`: the next generation will overwrite them. To import from the sprite-module root, create a barrel: +Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change anything in `.svg-sprite`: the next generation will overwrite it. In modes other than bare `standalone`, the generated `.gitignore` is also managed and must not be edited manually. To import from the sprite-module root, create a barrel: ```ts export * from './.svg-sprite' ``` -The generator owns `.gitignore` and files inside `.svg-sprite`. The `state.json` registry allows stale generated files to be removed, but the writer refuses to overwrite or delete any file without a generated marker. Do not remove the marker, bypass the refusal, or replace generated paths with symlinks: move the user-owned file or choose a different directory. +The generator owns the complete `.svg-sprite` directory and replaces it on every run. Never put user files inside it. The generator also owns `.gitignore` when the selected mode creates it; bare `standalone` leaves an existing `.gitignore` untouched. Generated paths must not contain symlinks. -The internal `index.js` exports the component from `react/react-component.js` and the readonly name array; the adjacent `index.d.ts` adds props/style types and the icon-name union. The manifest contains the mode, URL, target, icon list, and icon metadata for debug tools and is not imported by the production component. +The internal `index.js` exports the component from `react/react-component.js` and the readonly name array; the adjacent `index.d.ts` adds props/style types and the icon-name union. Bundler-mode manifest declarations define their types locally and do not import the generator package. The manifest contains the mode, URL, target, icon list, and icon metadata for debug tools and is not imported by the production component. The sprite remains a separate content-hashed asset; SVG path data is not embedded in JavaScript: - `react@vite` generates a static `sprite.svg?no-inline` import, preventing Vite from inlining it; -- `standalone@vite` uses the same Vite asset mechanism but exports an href helper without React; -- `standalone@webpack` uses Webpack Asset Modules without React; +- `standalone@vite` uses the same Vite asset mechanism and exports an href helper plus a native Web Component without React; +- `standalone@webpack` uses Webpack Asset Modules and exports the same mode-local Web Component without React; - React Webpack 5 and all Next modes generate `new URL('./sprite.svg', import.meta.url).href`, which must be processed by the selected bundler's Asset Modules; - a custom Webpack SVG loader must not intercept the generated `sprite.svg`; - in Next mode, the generated component does not contain `'use client'` and works in Server Components, SSR, and SSG; do not add a client boundary solely for an icon; diff --git a/skills/svg-sprites/src/en/core/50-usage-and-colors.md b/skills/svg-sprites/src/en/core/50-usage-and-colors.md index 0a75c00..72acb83 100644 --- a/skills/svg-sprites/src/en/core/50-usage-and-colors.md +++ b/skills/svg-sprites/src/en/core/50-usage-and-colors.md @@ -1,6 +1,20 @@ ## Usage, accessibility, and colors -The component name depends on the specific sprite's `name`. These examples use `name: 'file-manager'`, so the generated component is called `FileManagerIcon`. For `name: 'navigation'`, use the generated `NavigationIcon`. +The component name depends on the specific sprite's `name`. In `standalone@vite` and `standalone@webpack`, `name: 'file-manager'` creates the `` tag and the `defineFileManagerIconElement()` function: + +```ts +import { defineFileManagerIconElement } from './svg-sprite' + +defineFileManagerIconElement() +``` + +```html + +``` + +The native element has no runtime dependencies, selects the generated ID and `viewBox`, obtains the URL through the bundler, and renders `` in Shadow DOM. Its `icon` property is typed with the exact name union, while plain HTML attribute values are validated only at runtime. It defaults to `1em × 1em`; resize the host with CSS. Bare `standalone` does not generate a Web Component. + +In React/Next.js, the same `name: 'file-manager'` creates the `FileManagerIcon` React component. For `name: 'navigation'`, use the generated `NavigationIcon`. Import the component from the root of its sprite directory. `width` and `height` are optional: ordinary CSS classes can control the size. @@ -64,10 +78,10 @@ The `removeSize`, `replaceColors`, and `addTransition` transforms are enabled by Automatic replacement targets `fill`/`stroke` attributes and inline `style`. The values `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced. Check CSS classes and external stylesheets, gradients, patterns, filters, and `url(#...)` against the actual output. Page variables work through ``, but do not cross into an external document loaded through `` or `background-image`; a CSS mask preserves only a monochrome silhouette. -`SpriteViewer` is optional. Import it from `@gromlab/svg-sprites/react` only on a debug route: +`SpriteViewer` is optional. Install `@gromlab/svg-sprites` as a development dependency only when the project needs the Viewer, then import it from `@gromlab/svg-sprites/react` on a debug route: - in Vite, pass the result of a string-literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`; -- in Webpack, pass an array of static `() => import('.../manifest')` loaders; +- in Webpack, pass an array of static `() => import('.../.svg-sprite/svg-sprite.manifest.js')` loaders; - in Next.js, use the same static loaders, and for the App Router put the Viewer in a separate file with `'use client'`. -The Viewer accepts manifests/loaders and provides search, themes, colors, and examples, but production components do not depend on it. Import it from the package already installed as a development dependency. +The Viewer accepts manifests/loaders and provides search, themes, colors, and examples, but production components do not depend on it. diff --git a/skills/svg-sprites/src/en/core/60-verification.md b/skills/svg-sprites/src/en/core/60-verification.md index a2251a2..70a8e5f 100644 --- a/skills/svg-sprites/src/en/core/60-verification.md +++ b/skills/svg-sprites/src/en/core/60-verification.md @@ -3,7 +3,7 @@ After changing a config or SVG, perform these required quick checks: 1. Run the exact sprite command, for example `npm run sprite:file-manager`; it must exit with code `0` and report the name, icon count, mode, and `.svg-sprite` directory. -2. Confirm that `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, the `icon-data.js`/`.d.ts` and manifest `.js`/`.d.ts` pairs, `react/react-component.js`, its `.d.ts` and CSS Module, and `state.json` exist. +2. Confirm that `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, the `icon-data.js`/`.d.ts` and manifest `.js`/`.d.ts` pairs, `react/react-component.js`, its `.d.ts` and CSS Module exist. 3. Confirm that the new icon appears in the readonly name array and is accepted by the `icon` prop. 4. Run the project's existing type check, for example `npm run typecheck`. 5. Check that `target` in `.svg-sprite/svg-sprite.manifest.js` matches the selected mode key; the generated asset expression must use `?no-inline` for Vite and `new URL(...)` for Webpack/Next. diff --git a/skills/svg-sprites/src/en/core/70-diagnostics.md b/skills/svg-sprites/src/en/core/70-diagnostics.md index e1a1dff..e056031 100644 --- a/skills/svg-sprites/src/en/core/70-diagnostics.md +++ b/skills/svg-sprites/src/en/core/70-diagnostics.md @@ -8,9 +8,10 @@ Match the symptom to the relevant check and fix the root cause: | `Expected one config file or module directory` | Multiple paths were passed | Create one command per sprite and combine the scripts. | | `Sprite mode is required` | Mode is absent from both config and CLI | Add `mode` to the object or pass the full `--mode`. | | `Unsupported sprite config extension` | The supplied file is not `.ts`, `.js`, or `.json` | Use a supported config format. | -| `Input directory does not exist` | The relative path is wrong or an explicitly configured folder is missing | Resolve it from the config directory; create the folder or correct `inputFolder`. | -| Icons from a subdirectory are missing | Recursive scanning was assumed | Move the SVG to the top level of `inputFolder` or list it in `inputFiles`. | -| `SVG file does not exist`, `File is not an SVG`, or an empty input set | Invalid `inputFiles`, extension, or sources | Correct the path/extension and provide at least one input SVG. | +| A positive input source has no SVG matches | A folder is missing or empty, a glob matches nothing, or an exact path is missing or not an SVG | Resolve the source from the config directory and correct `input`; every positive item must produce at least one SVG. | +| Icons from a subdirectory are missing | A folder source was expected to scan recursively | Use an explicit glob such as `./icons/**/*.svg`; folders are shallow. | +| An excluded icon is still present | The exclusion lacks a leading `!`, is not in the `input` array, or is relative to the wrong directory | Add a matching `!` item and resolve it from the config directory. | +| CLI source selection is incomplete | Multiple sources were packed into one `--input` value or an option was omitted | Repeat `--input ` once per source or exclusion. | | Icon name or SVG ID collision | Two different files have the same basename, or a hash ID collides with a name | Rename one source SVG; do not select a file implicitly. | | `Refusing to overwrite/delete a user file` | A user file occupies a managed path or lost its marker | Do not bypass the protection: move the file or choose another sprite directory and regenerate. | | Missing `.svg-sprite/index.js` or name absent from autocomplete | Generation did not run, the user barrel does not export `.svg-sprite`, or the type server cached an old module | Run the sprite command, check `export * from './.svg-sprite'`, then typecheck; restart the TypeScript server if necessary. | diff --git a/skills/svg-sprites/src/en/references/complex-svg.md b/skills/svg-sprites/src/en/references/complex-svg.md index 0d6cfe7..c3d5db3 100644 --- a/skills/svg-sprites/src/en/references/complex-svg.md +++ b/skills/svg-sprites/src/en/references/complex-svg.md @@ -131,7 +131,7 @@ It is preserved as the fragment ID. Other names, such as `folder open.svg` or `2 Do not manually construct `#folder open`. Use the generated component or `.svg-sprite/svg-sprite.manifest.js`, which records both `name` and the actual `id`. -Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; `inputFiles` order does not select a winner. +Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; source order or overlap never selects a winner. ## Rendering method @@ -173,4 +173,4 @@ External stack-fragment support and paint-server behavior can vary across browse - A manual fragment fails for a name containing spaces: use the ID from the manifest. - One complex icon requires different transforms: move it to a separate sprite; per-icon transform config is not supported. -Target-specific execution and verification are documented in [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), and [next-pages.md](next-pages.md). +Target-specific execution and verification are documented in the exact-mode files under [guides](guides/standalone.md). diff --git a/skills/svg-sprites/src/en/references/next-app.md b/skills/svg-sprites/src/en/references/next-app.md deleted file mode 100644 index 44e8413..0000000 --- a/skills/svg-sprites/src/en/references/next-app.md +++ /dev/null @@ -1,149 +0,0 @@ -# Next.js App Router: operational reference - -## When to use this reference - -Use this document when the application uses `app/` or `src/app/` and needs a generated component for Server Components, SSR, or SSG. First determine the actual bundler: Turbopack and Webpack use different targets. For `pages/`, use [next-pages.md](next-pages.md). - -## Target matrix - -| Bundler | CLI mode | -|---|---| -| Turbopack | `next@app/turbopack` | -| Webpack 5 | `next@app/webpack` | - -Do not infer the target only from the presence of `next.config.*`. Check the actual flags in `dev`/`build`. The generator mode and verification-build bundler must agree. - -## Prepare the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -- The project chooses the directory for each specific sprite; it does not have to be a module/feature directory. Each config describes one of potentially many application sprites. -- `svg-sprite.config.ts` is the recommended name; the CLI accepts an explicitly selected `.ts`, `.js`, or `.json` file with any name. -- Paths are relative to its directory; `inputFolder` scanning is shallow. -- The local folder and `inputFiles` are merged. Equal basenames from different files are forbidden. -- If no name is configured, it is derived from the directory; for a directory named `svg-sprite`, the parent directory name is used. An explicit `name` is more stable when files move. -- `name` must use kebab-case and begin with a letter. -- `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`. -- The Next preset always generates `stack` and includes a root sprite `viewBox`; this is not a user setting. - -## Generation - -Example scripts for Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -For Webpack, replace only the complete mode with `next@app/webpack`. Do not run both targets sequentially for one directory: the second generation overwrites the first target's files. Run `npm run sprite:file-manager` for the first generation. - -## Server Component - -The generated production component does not contain `'use client'`. Import it directly into a server `page.tsx` or `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. Do not add a Client Component boundary solely for the icon. The generated module builds the asset URL with static `new URL('./sprite.svg', import.meta.url).href`; the same mechanism is used during SSR and in the browser. - -Do not move the SVG into `public`. The generator rewrites `.svg-sprite` and `.gitignore`; the root `index.ts` is application-owned and may contain `export * from './.svg-sprite'`. - -## SpriteViewer - -The Viewer is interactive and imported from a client-only entry. Create a separate Client Component page or child component: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), - () => import('@/ui/navigation/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -The `import()` paths must be string literals. Keep production icon imports outside `@gromlab/svg-sprites/react`: the package React entry is only for the Viewer and contains `'use client'`. - -## Verification - -Start with generation and typechecking: - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -If the target or Next build/deployment pipeline changed, or a runtime issue is being diagnosed, run the project's production build configured for the selected bundler: - -```bash -npm run build -``` - -After the required generation and typecheck, verify that: - -- `.svg-sprite/svg-sprite.manifest.js` contains the exact target `next@app/turbopack` or `next@app/webpack`; -- the server page compiles without adding `'use client'`. - -During a conditional production/runtime check, also verify that: - -- HTML contains an `href` to `.svg#id`, and the asset URL is available after `next start`; -- the URL does not use `data:`, `file:`, or `blob:`; -- SSR markup and the hydrated page refer to the same asset; -- complex SVGs were examined according to [complex-svg.md](complex-svg.md); claim visual and accessibility results only when the corresponding tools are available. - -## Common failures - -- `Next.js mode requires a router and bundler`: `--mode next` is invalid; specify the full mode. -- Build passes with one bundler but the runtime asset breaks with another: regenerate for the target actually used by the build. -- Webpack was selected, but the build used Turbopack: use the project's Webpack build command and `next@app/webpack`. -- Viewer causes a Server Component error: the Viewer file needs `'use client'`; the generated icon component does not. -- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file. -- Two CI jobs generate different targets in one checkout: separate their directories or ensure each job uses one consistent target. -- Asset is missing under `basePath`/CDN: inspect Next asset handling and deployment configuration; do not replace the generated URL manually. -- Package subpath type error: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -To invoke the generator from a build script, open [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/en/references/next-pages.md b/skills/svg-sprites/src/en/references/next-pages.md deleted file mode 100644 index a24d531..0000000 --- a/skills/svg-sprites/src/en/references/next-pages.md +++ /dev/null @@ -1,138 +0,0 @@ -# Next.js Pages Router: operational reference - -## When to use this reference - -Use this document for pages under `pages/` or `src/pages/`, including SSR with `getServerSideProps`, SSG, and client-side navigation. If the route is under `app/`, use [next-app.md](next-app.md). - -## Selecting a target - -| Bundler | CLI mode | -|---|---| -| Turbopack | `next@pages/turbopack` | -| Webpack 5 | `next@pages/webpack` | - -Determine the actual script flags before selecting a mode. The presence of the Pages Router does not automatically imply Webpack; select the target from the bundler used by the project. - -## Structure and config - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/webpack', - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -The project chooses the directory containing each specific sprite config, and it does not have to be a module/feature directory; each config describes one of potentially many application sprites. - -All source paths are resolved from the config directory. `inputFolder` defaults to `./icons`, and scanning is shallow. `inputFiles` is merged with the folder. The same path is deduplicated, but two different `check.svg` files conflict. An explicitly configured missing folder is an error even when `inputFiles` is non-empty. - -`name` uses kebab-case and determines the public names. `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`. Next modes always create `stack`, not `symbol`. - -## Commands - -Example lifecycle scripts for Webpack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -If pre-scripts already exist, add generation to their current command chain. Generated imports are absent from Git, so generation must precede TypeScript and Next compilation. For Turbopack, replace the complete mode with `next@pages/turbopack`. Run `npm run sprite:file-manager` for the first generation. - -## Usage in the Pages Router - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The component works during SSR, SSG, and client navigation. It uses `new URL('./sprite.svg', import.meta.url).href` so Next emits an external hashed asset. Do not copy the generated SVG into `public` or construct the URL manually. - -Import the component and types from the local user-owned `svg-sprite/index.ts`, which re-exports `.svg-sprite`. Do not edit `.svg-sprite` or the generated `.gitignore`. - -## SpriteViewer - -Pages Router components run on the client as well, so a separate `'use client'` directive is unnecessary: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), - () => import('@/ui/navigation/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -Use literal imports and expose the page only as a debug/internal tool. If the page participates in production routing, restrict access through the application's own controls. - -## Verification - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -If the target or Next build/deployment pipeline changed, or a runtime issue is being diagnosed, run the project's production build configured for the selected bundler: - -```bash -npm run build -``` - -After the required generation and typecheck, verify that: - -- `.svg-sprite/svg-sprite.manifest.js` contains `next@pages/turbopack` or `next@pages/webpack`; -- `getServerSideProps`/`getStaticProps` does not import the package React Viewer entry. - -After the conditional production build, and only when browser tools are available, inspect the SSR route and navigation to it: - -- initial HTML contains `.svg#id`; -- the SVG URL responds successfully and is not `data:`, `file:`, or `blob:`; -- after client navigation, the icon keeps the same valid URL; -- gradients, masks, and colors were examined according to [complex-svg.md](complex-svg.md); do not claim a visual or accessibility result without the necessary tools. - -## Common failures - -- Mode uses `next@app/...`: the module may generate, but its manifest and target contract are wrong; use `next@pages/...`. -- The command and mode select different bundlers: use the project's matching build command and regenerate. -- Viewer manifest is missing from the chunk: the `import()` path must be a string literal and must exist before the Next build. -- Icon appears after a full reload but disappears during navigation: check external asset availability with `basePath`, `assetPrefix`, and the production origin. -- `Refusing to overwrite a user file`: the sprite directory contains an unmanaged file with a reserved name; move it. -- Icon name is absent from the type: folder scanning is not recursive; move the SVG to the top level or add its exact path to `inputFiles`. - -For programmatic generation, use [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/en/references/programmatic-api.md b/skills/svg-sprites/src/en/references/programmatic-api.md index 42727c0..fc6344b 100644 --- a/skills/svg-sprites/src/en/references/programmatic-api.md +++ b/skills/svg-sprites/src/en/references/programmatic-api.md @@ -18,23 +18,28 @@ await generateSprite('src/ui/icons/svg-sprite.config.ts') await generateSprite('src/ui/icons', { mode: 'react@vite', name: 'app', - inputFolder: './icons', + input: './icons', }) ``` A directory enables config-less mode. `mode` must exist after settings are merged. +`input?: string | string[]` defaults to `./icons`. Each value is a folder, an exact SVG file, or a glob, resolved from the config directory or the config-less source directory. Folder scans are shallow; recurse only with an explicit glob such as `./icons/**/*.svg`. Arrays combine sources, and items prefixed with `!` exclude matches. Every positive item must resolve to at least one SVG. The final files are deduplicated and sorted, while different files with the same basename cause an error. + ## Overrides ```ts await generateSprite('src/ui/icons/custom.json', { mode: 'react@webpack', - inputFiles: ['../../shared/search.svg'], + input: [ + '../../shared/icons/**/*.svg', + '!../../shared/icons/legacy-*.svg', + ], transform: { addTransition: false }, }) ``` -Values are applied as `defaults → config → API overrides`. `transform` is merged by field; a supplied `inputFiles` replaces the config array. +Values are applied as `defaults → config → API overrides`. `transform` is merged by field; a supplied `input` replaces the config value. The specialized `generateReactSprite` and `generateNextSprite` functions remain as compatibility wrappers, but prefer `generateSprite` in new code. diff --git a/skills/svg-sprites/src/en/references/react-vite.md b/skills/svg-sprites/src/en/references/react-vite.md deleted file mode 100644 index f4daf52..0000000 --- a/skills/svg-sprites/src/en/references/react-vite.md +++ /dev/null @@ -1,166 +0,0 @@ -# React with Vite: operational reference - -## When to use this reference - -Use this document when the project uses React without Next.js and is built with Vite, or when the generated component contains a `sprite.svg?no-inline` import. Do not apply this target to Webpack; use [react-webpack.md](react-webpack.md) instead. - -## Establish context first - -1. Check `package.json`: confirm React, Vite, and the actual `dev`, `build`, and `typecheck` commands. -2. Find existing `svg-sprite.config.ts` files and scripts containing `svg-sprites`. Do not create a second directory for an existing sprite. -3. Choose a target directory for the specific sprite. It does not have to be a module or feature directory; pass the full path to its config file. -4. Do not manually edit `.svg-sprite` or the local `.gitignore`; the generator owns them. The root `index.ts` is application-owned. - -Minimal structure: - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -## Setup - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -`src/ui/file-manager/svg-sprite/svg-sprite.config.ts`: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -- Each config describes one specific sprite; an application may contain many independent configs and sprites. -- Every config path is resolved relative to the directory containing `svg-sprite.config.ts`. -- `inputFolder` defaults to `./icons`; folder scanning is shallow and includes files ending in `.svg`. -- `inputFolder` and `inputFiles` are merged, and duplicate absolute paths are deduplicated. -- If `inputFiles` is non-empty and the implicit `./icons` directory is absent, generation uses only the list. An explicitly configured missing `inputFolder` is always an error. -- Different files with the same basename, such as two `check.svg` files, conflict because they define the same public icon name. -- `name` must use kebab-case and begin with an ASCII letter. `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames` below are only examples of generated names for `name: 'file-manager'`. -- The React preset always creates the `stack` format; `symbol` cannot be selected here. - -## Command and scripts - -Add the local CLI to `package.json` and run it before processes that need generated imports: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -If the project already has `predev` or `prebuild`, integrate generation into the existing orchestration instead of overwriting the script. - -Run `npm run sprite:file-manager` for the first generation. - -## Usage - -Import only from the public local entry point: - -```tsx -import { FileManagerIcon, fileManagerIconNames } from './svg-sprite' -import type { FileManagerIconName, FileManagerIconStyle } from './svg-sprite' - -const colorStyle: FileManagerIconStyle = { - '--icon-color-1': '#2563eb', -} - -export function FolderIcon({ icon }: { icon: FileManagerIconName }) { - return -} - -export const availableIcons = fileManagerIconNames -``` - -`width` and `height` are optional in JSX; a CSS class can control the size. Without `wrapped`, the component renders `` and accepts SVG attributes. With `wrapped={true}`, the root becomes a `` and the inner SVG fills its width and height: - -```tsx - -``` - -For normal imports, use a user-owned barrel with `export * from './.svg-sprite'`; the remaining files in the hidden directory are internal. - -## Target-specific behavior - -The Vite target generates a static import: - -```ts -import spriteUrl from './sprite.svg?no-inline' -``` - -The `?no-inline` query is required: it prevents Vite from converting a small SVG into a data URL. Do not remove the query or copy the generated SVG into `public`; Vite must emit a separate content-hashed asset. - -Use the same mechanism for low-level `` integration: - -```tsx -import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline' - - - - -``` - -A manual `#check` fragment is safe only for names matching `^[a-zA-Z][a-zA-Z0-9_-]*$`. For spaces and other characters, the generated component uses a stable hash ID; the exact ID is available in `.svg-sprite/svg-sprite.manifest.js`. - -## SpriteViewer - -After generation, add the Viewer only to a debug route: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export function IconsDebugPage() { - return -} -``` - -The `import.meta.glob` argument must remain a string literal. Generation must finish before Vite starts, or new manifests will not be included in the glob. - -## Verification - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -These are the required quick checks. Inspect the generated files statically: - -- the user-owned `index.ts` re-exports the component, props, style, name union, and runtime array from `.svg-sprite`; -- `.svg-sprite/svg-sprite.manifest.js` contains `target: "vite"`, `format: "stack"`, and the expected icon count. - -If the target or asset pipeline changed, or a runtime issue is being diagnosed, also run the production build. When browser tools are available, inspect Network for a separate `.svg` asset rather than `data:image/svg+xml`, a successful URL, and ``. For complex colors, `defs`, and dimensions, follow [complex-svg.md](complex-svg.md); do not claim a visual or accessibility result without the necessary tools. - -## Common failures - -- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file. -- `Sprite mode is required`: set `mode: 'react@vite'` in the config or pass `--mode react@vite`. -- Icon missing from autocomplete: check the case-sensitive `.svg` suffix and shallow location, then regenerate before typechecking. -- `Refusing to overwrite a user file`: do not remove the marker or bypass the writer; move the user-owned file or choose another sprite directory. -- Viewer is empty: check the literal glob, the existence of `.svg-sprite/svg-sprite.manifest.js`, and the `predev` execution order. -- SVG was inlined: confirm that the module targets `vite` and its import still contains `?no-inline`. -- TypeScript cannot resolve the package subpath: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -For execution without the CLI, use [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/en/references/react-webpack.md b/skills/svg-sprites/src/en/references/react-webpack.md deleted file mode 100644 index 46c71ac..0000000 --- a/skills/svg-sprites/src/en/references/react-webpack.md +++ /dev/null @@ -1,152 +0,0 @@ -# React with Webpack 5: operational reference - -## When to use this reference - -Use this document for a React application built with Webpack 5 when SVGs must be processed through Asset Modules. If the project uses Vite, see [react-vite.md](react-vite.md). For Next.js, do not select `react@webpack`; router-specific targets are documented in [next-app.md](next-app.md) and [next-pages.md](next-pages.md). - -## Diagnose the environment before making changes - -1. Confirm Webpack major version 5 in `package.json` or the lockfile. -2. Inspect `.svg` entries in `module.rules`, `output.publicPath`, the dev server, and existing asset conventions. -3. Find existing `svg-sprite.config.ts` files and generation scripts. -4. Choose the project directory for the specific sprite. It does not have to match a module or feature directory. Pass the full path to its config file. - -Minimal structure: - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -## Configuration and generation - -Install the package as a development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@webpack', - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -Each config describes one of potentially many independent application sprites. - -Paths are relative to `svg-sprite.config.ts`. The folder is scanned only at its top level. `inputFolder` and `inputFiles` are merged; duplicate paths are deduplicated, but equal basenames from different files cause an ID conflict. The implicit `./icons` folder may be absent when `inputFiles` is non-empty; an explicitly configured missing folder is an error. `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`. - -Recommended lifecycle hooks: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Do not overwrite existing pre-scripts; add generation to their current command chain. Run `npm run sprite:file-manager` for the first generation. The React preset always emits `stack`. - -## Public component - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export function OpenFolderButton() { - return ( - - ) -} -``` - -`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The user-owned barrel also exports `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName`, and `fileManagerIconNames`. Do not edit `.svg-sprite` or the generated `.gitignore`. - -## Webpack target behavior - -The generated component obtains its URL only through this static expression: - -```ts -const spriteUrl = new URL('./sprite.svg', import.meta.url).href -``` - -Webpack 5 recognizes it as an Asset Module and replaces it with the public URL of a separate SVG. For correct handling: - -- do not move the path into a variable or modify the generated expression; -- ensure Babel/TypeScript does not transform `import.meta.url` before Webpack processes it; -- do not let `@svgr/webpack`, `svg-inline-loader`, `raw-loader`, or a general SVG rule intercept `svg-sprite/.svg-sprite/sprite.svg`; -- with custom rules, either exclude the generated sprite from component/raw loaders or add a dedicated `type: 'asset/resource'` rule; -- check `output.publicPath`, especially for CDN, subpath, and dev-server deployments. - -Example dedicated rule when an existing loader intercepts every SVG: - -```js -{ - test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/, - type: 'asset/resource', -} -``` - -Integrate this rule with the project's current configuration; do not add a duplicate matcher when standard Asset Modules already handle `new URL` correctly. - -## SpriteViewer - -Webpack does not provide `import.meta.glob`. Pass static lazy imports with string-literal paths: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export function IconsDebugPage() { - return -} -``` - -A dynamically assembled `import(path)` is not suitable: Webpack cannot reliably associate the manifests with their assets. Expose the Viewer only on a debug/internal route. - -## Verification - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -These are the required quick checks. After generation, verify that: - -- `.svg-sprite/svg-sprite.manifest.js` contains `target: "webpack"` and `format: "stack"`; -- the generated component contains `new URL('./sprite.svg', import.meta.url).href`. - -A production build and browser/Network checks are additionally required when the target, Webpack asset rules, `publicPath`/deployment pipeline changed, or a runtime issue is being diagnosed. In that case, verify a separate hashed SVG asset, an HTTP(S) `.svg#id` URL, content type, fragment ID, and the correct CDN/subpath URL. Do not claim visual or accessibility correctness without the necessary tools. - -For icons with gradients, filters, masks, or internal CSS, perform the checks in [complex-svg.md](complex-svg.md). - -## Common failures - -- `Unsupported React target`: the programmatic API received a value other than `'webpack'`; the CLI mode must be exactly `react@webpack`. -- Webpack tries to render the SVG as a React component: the generated sprite matched the SVGR rule; exclude it or prioritize `asset/resource`. -- URL points to the wrong host/subpath: fix `output.publicPath` and runtime deployment settings, not the generated file. -- `import.meta` is unsupported: confirm the build really uses Webpack 5 and that an intermediate transpiler preserves the expression. -- Viewer does not load the manifest: check the literal path, chunk loading, and generation before compilation. -- `Refusing to overwrite a user file`: the directory already contains a user-owned `.gitignore` or file in `.svg-sprite`; move it rather than bypassing protection. -- Icon color does not change: use `color` for monochrome icons or `--icon-color-N` through ``; page CSS does not cross into ``. - -For custom build orchestration, see [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/SKILL.md b/skills/svg-sprites/src/ru/SKILL.md index 6b0c635..ffe0ca6 100644 --- a/skills/svg-sprites/src/ru/SKILL.md +++ b/skills/svg-sprites/src/ru/SKILL.md @@ -11,8 +11,9 @@ ## Справочники по необходимости -- Для React + Vite открой [React + Vite](./references/react-vite.md), для React с собственным Webpack 5 — [React + Webpack](./references/react-webpack.md). -- Для Next.js открой руководство для [App Router](./references/next-app.md) или [Pages Router](./references/next-pages.md) и выбери раздел фактического сборщика. +- Для статической публикации открой [bare standalone](./references/guides/standalone.md). Для vanilla-приложений со сборщиком открой [standalone + Vite](./references/guides/standalone-vite.md) или [standalone + Webpack](./references/guides/standalone-webpack.md). +- Для React открой exact guide для [Vite](./references/guides/react-vite.md) или [Webpack](./references/guides/react-webpack.md). +- Для Next.js App Router открой exact guide для [Turbopack](./references/guides/next-app-turbopack.md) или [Webpack](./references/guides/next-app-webpack.md). +- Для Next.js Pages Router открой exact guide для [Turbopack](./references/guides/next-pages-turbopack.md) или [Webpack](./references/guides/next-pages-webpack.md). - Для вызова генератора из Node.js открой [программный API](./references/programmatic-api.md). - Для gradients, filters, `url(#...)`, нестандартных цветов и проблем с `viewBox` открой [сложные SVG](./references/complex-svg.md). -- Полную пользовательскую документацию используй как вторичный источник: [README пакета](./references/upstream/README_RU.md). diff --git a/skills/svg-sprites/src/ru/core/00-package-overview.md b/skills/svg-sprites/src/ru/core/00-package-overview.md index cf2e412..ffa5f06 100644 --- a/skills/svg-sprites/src/ru/core/00-package-overview.md +++ b/skills/svg-sprites/src/ru/core/00-package-overview.md @@ -1,15 +1,16 @@ ## Что делает пакет -`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, поддерживает standalone-проекты и для React/Next.js создаёт типизированный компонент. +`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js. Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные: - SVG asset; - mode-specific manifest data; - для bundler modes — типы имён и production entry `.svg-sprite/index.js`; +- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации; - только для React/Next.js — React-компонент; - для bare `standalone` — deployment-neutral JSON manifest без публичного URL. Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Имена `FileManagerIcon` и `fileManagerIconNames` ниже являются примерами API одного из возможных спрайтов, а не фиксированными экспортами пакета. -Generated production runtime не импортирует `@gromlab/svg-sprites` во время выполнения. Устанавливай пакет как development dependency, чтобы config helpers и локальный CLI использовали версию из lockfile проекта. +Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация работает через `npx --package` с зафиксированной версией без добавления package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API. diff --git a/skills/svg-sprites/src/ru/core/10-mode-selection.md b/skills/svg-sprites/src/ru/core/10-mode-selection.md index bd35b6e..1791654 100644 --- a/skills/svg-sprites/src/ru/core/10-mode-selection.md +++ b/skills/svg-sprites/src/ru/core/10-mode-selection.md @@ -21,10 +21,10 @@ CLI принимает ровно один путь. Путь к файлу `.ts ```json { "scripts": { - "sprite:": "svg-sprites ", - "sprite::cli": "svg-sprites --mode " + "sprite:": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites ", + "sprite::cli": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --mode " } } ``` -Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. +Генерация через `npx` не добавляет package в проект. В CI укажи точную версию вместо `latest`. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. diff --git a/skills/svg-sprites/src/ru/core/20-project-inspection.md b/skills/svg-sprites/src/ru/core/20-project-inspection.md index 309b129..9cfaecd 100644 --- a/skills/svg-sprites/src/ru/core/20-project-inspection.md +++ b/skills/svg-sprites/src/ru/core/20-project-inspection.md @@ -9,14 +9,14 @@ 5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения. 6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`. -Все пути конфига считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога: +Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт: -- `inputFolder` по умолчанию равен `./icons`; -- `inputFiles` содержит дополнительные относительные пути к отдельным SVG и объединяется с `inputFolder`; -- одинаковый абсолютный файл дедуплицируется, но разные файлы с одинаковым basename иконки вызывают ошибку; -- сканирование `inputFolder` плоское: читаются только непосредственные файлы с окончанием `.svg`, вложенные каталоги не обходятся; -- отсутствующая явно заданная `inputFolder` является ошибкой; -- если `inputFolder` не задана, `./icons` отсутствует, а `inputFiles` непустой, генерация работает только по `inputFiles`; -- пустой итоговый набор, отсутствующий файл или путь не к `.svg` является ошибкой. +- `input?: string | string[]` по умолчанию равен `./icons`; +- каждая строка задаёт папку, точный SVG-файл или glob; +- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`; +- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора; +- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой; +- разрешённые файлы дедуплицируются и детерминированно сортируются; +- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников. -Не копируй общий SVG в несколько папок: добавь его относительный путь в `inputFiles` каждого нужного спрайта. Если требуется рекурсивная структура, перечисли файлы явно или измени структуру источников; генератор не выполняет recursive scan. +Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения. diff --git a/skills/svg-sprites/src/ru/core/30-react-next-setup.md b/skills/svg-sprites/src/ru/core/30-react-next-setup.md index 8214bad..30b1714 100644 --- a/skills/svg-sprites/src/ru/core/30-react-next-setup.md +++ b/skills/svg-sprites/src/ru/core/30-react-next-setup.md @@ -12,27 +12,21 @@ src/ui/file-manager/svg-sprite/ Один `svg-sprite.config.ts` создаёт один независимый спрайт. Для нескольких наборов выбери несколько каталогов и дай каждому уникальное `name`. -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -Используй config helper для autocomplete и проверки типов: +Генератор не нужно устанавливать в проект. Начни с plain config без package +import: ```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ +export default { mode: 'react@vite', name: 'file-manager', description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../shared/icons/close.svg'], -}) + input: ['./icons', '../../shared/icons/close.svg'], +} ``` -Контракт объекта одинаков для React и Next.js; отличается полный `mode`. +`input` принимает одну папку, точный SVG-файл или glob либо массив, объединяющий эти источники. Элемент массива с префиксом `!` исключает совпадения. Папки сканируются плоско; для рекурсии нужен явный glob `**/*.svg`. Если `input` не задан, используется `./icons`. Все пути считаются от каталога конфига, и каждый positive-источник должен найти хотя бы один SVG. + +Контракт объекта одинаков для React и Next.js; отличается полный `mode`. Устанавливай package только для необязательного Viewer, программного API или package-типизации config. В exact guides также есть локальный copy-paste type для проектов без package. `name` должен начинаться с латинской буквы и записываться в kebab-case; из примера `file-manager` будут созданы `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`. Другой спрайт получает собственные имена. Если `name` не задан, генератор выводит его из каталога. @@ -41,7 +35,7 @@ export default defineSpriteConfig({ ```json { "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", + "sprite:file-manager": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", "sprites": "npm run sprite:file-manager" } } @@ -49,9 +43,18 @@ export default defineSpriteConfig({ Для Next.js укажи в config полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:` на каждый config-файл и последовательно вызови их из `sprites`. -Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Добавь генерацию к `predev`, `prebuild` и, если есть `typecheck`, к `pretypecheck`. +Чтобы задать источники через CLI, повторяй `--input `; значения образуют тот же массивный контракт, включая исключения с `!`: -Если lifecycle script отсутствует, создай его. Если он уже существует, сохрани его команду и допиши генерацию через `&&`, например преобразуй `"prebuild": "npm run lint"` в `"prebuild": "npm run lint && npm run sprites"`. Никогда не заменяй существующий `pre*` одной генерацией и не создавай второй одноимённый JSON key. +```bash +npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts \ + --input ./icons \ + --input '../../shared/icons/**/*.svg' \ + --input '!../../shared/icons/legacy-*.svg' +``` + +Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Generated declarations self-contained и не импортируют generator package. + +Запускай генерацию либо через `predev`/`prebuild`/`pretypecheck`, либо явно внутри соответствующих команд. Не используй обе формы для одной команды, иначе генерация выполнится дважды. Сохраняй существующие команды и не создавай второй одноимённый JSON key. Запусти первую генерацию вручную: diff --git a/skills/svg-sprites/src/ru/core/40-generated-contract.md b/skills/svg-sprites/src/ru/core/40-generated-contract.md index 13fa6a1..5561bfe 100644 --- a/skills/svg-sprites/src/ru/core/40-generated-contract.md +++ b/skills/svg-sprites/src/ru/core/40-generated-contract.md @@ -9,7 +9,6 @@ svg-sprite/ ├── index.ts # необязательный пользовательский barrel ├── .gitignore # управляет генератор └── .svg-sprite/ - ├── state.json # реестр владения и версия контракта ├── index.js ├── index.d.ts ├── icon-data.js @@ -25,23 +24,25 @@ svg-sprite/ Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно -генерируют `index.*`, `icon-data.*` и resolved manifest. +генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также +содержит нативный generated Web Component; bare `standalone` не получает JS runtime, +не создаёт и не изменяет `.gitignore`. -Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную `.gitignore` и содержимое `.svg-sprite`: повторная генерация их перезапишет. Для импорта из корня sprite-модуля создай barrel: +Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel: ```ts export * from './.svg-sprite' ``` -Генератор владеет `.gitignore` и файлами внутри `.svg-sprite`. Реестр `state.json` позволяет удалить устаревший generated-файл, но writer откажется перезаписывать или удалять файл без generated-маркера. Не удаляй маркер, не обходи отказ и не подменяй generated-пути symlink: перенеси пользовательский файл или выбери другой каталог. +Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт; bare `standalone` оставляет существующий `.gitignore` без изменений. Generated-пути не должны содержать symlink. -Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом. +Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом. Спрайт остаётся отдельным asset с content hash; SVG path-данные не встраиваются в JavaScript: - `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline; -- `standalone@vite` использует тот же Vite asset-механизм, но экспортирует href helper без React; -- `standalone@webpack` использует Webpack Asset Modules без React; +- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React; +- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React; - React Webpack 5 и все Next modes генерируют `new URL('./sprite.svg', import.meta.url).href`, который должен обработать Asset Modules соответствующего сборщика; - кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`; - в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки; diff --git a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md b/skills/svg-sprites/src/ru/core/50-usage-and-colors.md index 99f2974..39d712e 100644 --- a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md +++ b/skills/svg-sprites/src/ru/core/50-usage-and-colors.md @@ -1,6 +1,20 @@ ## Использование, доступность и цвета -Имя компонента зависит от `name` конкретного спрайта. В примерах используется `name: 'file-manager'`, поэтому generated-компонент называется `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`. +Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `` и функцию `defineFileManagerIconElement()`: + +```ts +import { defineFileManagerIconElement } from './svg-sprite' + +defineFileManagerIconElement() +``` + +```html + +``` + +Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует. + +В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`. Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом. @@ -64,10 +78,10 @@ Generated-компонент не выбирает семантику за пр Автозамена рассчитана на `fill`/`stroke` attributes и inline `style`. Значения `none`, `transparent`, `inherit`, `unset`, `initial` не заменяются. CSS-классы и внешние stylesheets, gradients, patterns, filters и `url(#...)` проверяй на реальном результате. Переменные страницы работают через ``, но не проникают во внешний документ при `` или `background-image`; CSS mask оставляет только одноцветный силуэт. -`SpriteViewer` необязателен. Подключай его из `@gromlab/svg-sprites/react` только на debug-маршруте: +`SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer, и подключай его из `@gromlab/svg-sprites/react` на debug-маршруте: - в Vite передай результат строкового literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`; -- в Webpack передай массив статических `() => import('.../manifest')`; +- в Webpack передай массив статических `() => import('.../.svg-sprite/svg-sprite.manifest.js')`; - в Next.js используй такие же статические loaders, а для App Router помести Viewer в отдельный файл с `'use client'`. -Viewer принимает manifests/loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят. Импортируй его из пакета, уже установленного как development dependency. +Viewer принимает manifests/loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят. diff --git a/skills/svg-sprites/src/ru/core/60-verification.md b/skills/svg-sprites/src/ru/core/60-verification.md index 82d7d65..a79e050 100644 --- a/skills/svg-sprites/src/ru/core/60-verification.md +++ b/skills/svg-sprites/src/ru/core/60-verification.md @@ -3,7 +3,7 @@ После изменения конфига или SVG выполни обязательные быстрые проверки: 1. Запусти точную sprite-команду, например `npm run sprite:file-manager`; процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`. -2. Проверь наличие `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, пары `icon-data.js`/`.d.ts`, manifest `.js`/`.d.ts`, `react/react-component.js`, его `.d.ts` и CSS Module, а также `state.json`. +2. Проверь наличие `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, пары `icon-data.js`/`.d.ts`, manifest `.js`/`.d.ts`, `react/react-component.js`, его `.d.ts` и CSS Module. 3. Убедись, что новая иконка присутствует в readonly-массиве имён и принимается prop `icon`. 4. Запусти существующую проверку типов проекта, например `npm run typecheck`. 5. Проверь в `.svg-sprite/svg-sprite.manifest.js`, что `target` совпадает с выбранным mode key; generated asset expression должен быть `?no-inline` для Vite и `new URL(...)` для Webpack/Next. diff --git a/skills/svg-sprites/src/ru/core/70-diagnostics.md b/skills/svg-sprites/src/ru/core/70-diagnostics.md index 88350a4..e29d895 100644 --- a/skills/svg-sprites/src/ru/core/70-diagnostics.md +++ b/skills/svg-sprites/src/ru/core/70-diagnostics.md @@ -8,9 +8,10 @@ | `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. | | `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. | | `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. | -| `Input directory does not exist` | Ошибка относительного пути или отсутствует явно заданная папка | Считай путь от каталога конфига; создай папку или исправь `inputFolder`. | -| Иконки из подпапки не появились | Ожидался recursive scan | Перемести SVG на верхний уровень `inputFolder` или перечисли его в `inputFiles`. | -| `SVG file does not exist`, `File is not an SVG` или пустой набор | Неверный `inputFiles`, расширение или источники | Исправь путь/расширение и обеспечь хотя бы один входной SVG. | +| Positive input-источник не нашёл SVG | Папка отсутствует или пуста, glob не совпал либо точный путь отсутствует или ведёт не к SVG | Разреши источник от каталога конфига и исправь `input`; каждый positive-элемент должен дать хотя бы один SVG. | +| Иконки из подпапки не появились | От папки ожидалось рекурсивное сканирование | Используй явный glob, например `./icons/**/*.svg`; папки сканируются плоско. | +| Исключённая иконка всё ещё присутствует | У исключения нет префикса `!`, оно находится не в массиве `input` или считается не от того каталога | Добавь совпадающий `!`-элемент и считай его от каталога конфига. | +| CLI выбрал не все источники | Несколько источников поместили в одно значение `--input` или пропустили option | Повтори `--input ` отдельно для каждого источника или исключения. | | Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. | | `Refusing to overwrite/delete a user file` | Пользовательский файл занял managed-путь или потерял marker | Не обходи защиту: перенеси файл либо выбери другой sprite-каталог и перегенерируй. | | Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Генерация не запускалась после изменения, пользовательский barrel не экспортирует `.svg-sprite` либо type server держит старый модуль | Запусти sprite-команду, проверь `export * from './.svg-sprite'`, затем typecheck; при необходимости перезапусти TypeScript server. | diff --git a/skills/svg-sprites/src/ru/references/complex-svg.md b/skills/svg-sprites/src/ru/references/complex-svg.md index acc304b..d1070c0 100644 --- a/skills/svg-sprites/src/ru/references/complex-svg.md +++ b/skills/svg-sprites/src/ru/references/complex-svg.md @@ -131,7 +131,7 @@ transform: { Не создавай вручную `#folder open`. Используй generated component либо `.svg-sprite/svg-sprite.manifest.js`, где записаны `name` и фактический `id`. -Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок `inputFiles` не является способом выбрать победителя. +Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок или пересечение источников не выбирают победителя. ## Способ отображения @@ -173,4 +173,4 @@ External stack fragment support и поведение paint servers могут - Ручной fragment не работает для имени с пробелом: используй ID из manifest. - Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует. -Target-specific запуск и проверка описаны в [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md) и [next-pages.md](next-pages.md). +Target-specific запуск и проверка описаны в exact-mode файлах каталога [guides](guides/standalone.md). diff --git a/skills/svg-sprites/src/ru/references/next-app.md b/skills/svg-sprites/src/ru/references/next-app.md deleted file mode 100644 index 5f97928..0000000 --- a/skills/svg-sprites/src/ru/references/next-app.md +++ /dev/null @@ -1,149 +0,0 @@ -# Next.js App Router: операционный reference - -## Когда открывать - -Открывай этот документ, если приложение использует каталог `app/` или `src/app/` и нужен generated-компонент для Server Components, SSR или SSG. Сначала установи реальный сборщик: target для Turbopack и Webpack различается. Для `pages/` используй [next-pages.md](next-pages.md). - -## Матрица target - -| Сборщик | CLI mode | -|---|---| -| Turbopack | `next@app/turbopack` | -| Webpack 5 | `next@app/webpack` | - -Не выводи target только из наличия `next.config.*`. Проверь фактические flags в `dev`/`build`. Mode генератора и сборщик контрольной сборки должны совпадать. - -## Подготовка каталога спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -- Каталог выбирает проект конкретного спрайта и не обязан быть module/feature-каталогом. Каждый config описывает один из потенциально многих спрайтов приложения. -- `svg-sprite.config.ts` — рекомендуемое имя; CLI принимает явно переданный `.ts`, `.js` или `.json` файл с любым именем. -- Пути считаются от его каталога; `inputFolder` сканируется нерекурсивно. -- Локальная папка и `inputFiles` объединяются. Одинаковые basename разных файлов запрещены. -- Если имя не задано, оно выводится из каталога: для папки `svg-sprite` берётся имя родительской папки. Явное `name` надёжнее при перемещениях. -- `name` должен быть kebab-case и начинаться с буквы. -- `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`. -- Next preset всегда генерирует `stack` и включает root `viewBox` спрайта; это не пользовательская настройка. - -## Генерация - -Пример scripts для Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Для Webpack замени только mode целиком на `next@app/webpack`. Не запускай два target последовательно для одного каталога: второй перезапишет generated target первого. Для первой генерации запусти `npm run sprite:file-manager`. - -## Server Component - -Generated production component не содержит `'use client'`. Импортируй его непосредственно в server `page.tsx` или `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Не добавляй Client Component boundary только ради иконки. Generated module формирует asset URL через статический `new URL('./sprite.svg', import.meta.url).href`; один механизм используется при SSR и в браузере. - -Не перемещай SVG в `public`. Генератор перезаписывает `.svg-sprite` и `.gitignore`; корневой `index.ts` принадлежит приложению и может содержать `export * from './.svg-sprite'`. - -## SpriteViewer - -Viewer интерактивен и импортируется из client-only entry. Создай отдельную Client Component страницу или дочерний компонент: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), - () => import('@/ui/navigation/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -Пути `import()` должны быть строковыми литералами. Оставь production icon imports за пределами `@gromlab/svg-sprites/react`: package React entry предназначен только для Viewer и содержит `'use client'`. - -## Проверка - -Сначала генерация и типы: - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -Если менялись target или Next build/deployment pipeline либо диагностируется runtime, запусти production-сборку проекта, настроенную на выбранный сборщик: - -```bash -npm run build -``` - -После обязательных генерации и typecheck проверь: - -- `.svg-sprite/svg-sprite.manifest.js` содержит точный target `next@app/turbopack` либо `next@app/webpack`; -- server page компилируется без добавления `'use client'`; - -При условной production/runtime-проверке проверь также: - -- HTML содержит `href` на `.svg#id`, а URL asset доступен после `next start`; -- URL не использует `data:`, `file:` или `blob:`; -- SSR markup и hydrated page ссылаются на один asset; -- сложные SVG исследованы по [complex-svg.md](complex-svg.md); визуальный и a11y результат утверждай только при наличии соответствующих инструментов. - -## Типовые ошибки - -- `Next.js mode requires a router and bundler`: нельзя использовать `--mode next`; укажи полный mode. -- Сборка проходит на одном bundler, а runtime asset ломается на другом: перегенерируй тем target, которым реально выполняется build. -- Выбран Webpack, но build ушёл в Turbopack: используй Webpack-команду проекта и `next@app/webpack`. -- Viewer вызывает ошибку Server Component: файл с Viewer должен иметь `'use client'`; generated icon component этого не требует. -- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу. -- Две CI jobs генерируют разные target в одном checkout: раздели каталоги или обеспечь один согласованный target на job. -- Asset не найден под `basePath`/CDN: проверяй Next asset handling и deployment config, не подменяй generated URL вручную. -- Ошибка package subpath types: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -Для вызова генератора из build script открой [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/references/next-pages.md b/skills/svg-sprites/src/ru/references/next-pages.md deleted file mode 100644 index d305cda..0000000 --- a/skills/svg-sprites/src/ru/references/next-pages.md +++ /dev/null @@ -1,138 +0,0 @@ -# Next.js Pages Router: операционный reference - -## Когда открывать - -Открывай этот документ для страниц в `pages/` или `src/pages/`, включая SSR через `getServerSideProps`, SSG и клиентские переходы. Если маршрут находится в `app/`, используй [next-app.md](next-app.md). - -## Выбор target - -| Сборщик | CLI mode | -|---|---| -| Turbopack | `next@pages/turbopack` | -| Webpack 5 | `next@pages/webpack` | - -Определи реальные flags scripts до выбора mode. Наличие Pages Router не означает автоматически Webpack: выбирай target по сборщику проекта. - -## Структура и конфиг - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/webpack', - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -Каталог с config выбирает проект конкретного спрайта и не обязан быть module/feature-каталогом; каждый config описывает один из потенциально многих спрайтов приложения. - -Все source paths разрешаются от каталога конфига. `inputFolder` по умолчанию `./icons`, сканирование не рекурсивно. `inputFiles` объединяется с папкой. Один и тот же путь дедуплицируется, но два разных `check.svg` конфликтуют. Явная отсутствующая папка считается ошибкой даже при заполненном `inputFiles`. - -`name` задаётся в kebab-case и определяет публичные имена. `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`. Next modes всегда создают `stack`, не `symbol`. - -## Команды - -Пример lifecycle для Webpack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Если pre-scripts уже существуют, добавь генерацию в текущую цепочку. Generated imports отсутствуют в Git, поэтому генерация должна предшествовать TypeScript и Next compilation. Для Turbopack замени mode целиком на `next@pages/turbopack`. Для первой генерации запусти `npm run sprite:file-manager`. - -## Использование в Pages Router - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Компонент работает при SSR, SSG и client navigation. Он использует `new URL('./sprite.svg', import.meta.url).href`, чтобы Next выпустил внешний hashed asset. Не копируй generated SVG в `public` и не конструируй URL вручную. - -Импортируй компонент и типы из локального пользовательского `svg-sprite/index.ts`, который переэкспортирует `.svg-sprite`. Не редактируй `.svg-sprite` и созданный `.gitignore`. - -## SpriteViewer - -Pages Router компоненты выполняются и на клиенте, поэтому отдельная директива `'use client'` не нужна: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), - () => import('@/ui/navigation/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -Используй literal imports и размещай страницу только как debug/internal tool. Если page участвует в production routing, ограничь доступ средствами самого приложения. - -## Проверка - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -Если менялись target или Next build/deployment pipeline либо диагностируется runtime, запусти production build проекта, настроенный на выбранный сборщик: - -```bash -npm run build -``` - -После обязательных генерации и typecheck проверь: - -- `.svg-sprite/svg-sprite.manifest.js` содержит `next@pages/turbopack` либо `next@pages/webpack`; -- `getServerSideProps`/`getStaticProps` не импортируют package React Viewer entry. - -После условного production build и только при наличии браузерных инструментов проверь SSR route и переход на него: - -- initial HTML содержит `.svg#id`; -- SVG URL отвечает успешно и не является `data:`, `file:` или `blob:`; -- после client navigation иконка сохраняет тот же корректный URL; -- gradients, masks и цвета исследованы по [complex-svg.md](complex-svg.md); не утверждай визуальный или a11y результат без доступных инструментов. - -## Типовые ошибки - -- В mode указан `next@app/...`: модуль может сгенерироваться, но manifest и контракт target неверны; используй `next@pages/...`. -- Command и mode выбирают разные bundler: используй соответствующую build-команду проекта и перегенерируй. -- Viewer manifest не попадает в chunk: путь `import()` должен быть строковым литералом и существовать до Next build. -- Иконка есть после full reload, но пропадает при переходе: проверь доступность внешнего asset с `basePath`, `assetPrefix` и production origin. -- `Refusing to overwrite a user file`: sprite-каталог содержит неуправляемый файл с зарезервированным именем; перенеси его. -- Имя иконки отсутствует в типе: папка не сканируется рекурсивно; перемести SVG на первый уровень либо добавь точный путь в `inputFiles`. - -Для программной генерации используй [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/references/programmatic-api.md b/skills/svg-sprites/src/ru/references/programmatic-api.md index 93a9686..e6b1b7f 100644 --- a/skills/svg-sprites/src/ru/references/programmatic-api.md +++ b/skills/svg-sprites/src/ru/references/programmatic-api.md @@ -18,23 +18,28 @@ await generateSprite('src/ui/icons/svg-sprite.config.ts') await generateSprite('src/ui/icons', { mode: 'react@vite', name: 'app', - inputFolder: './icons', + input: './icons', }) ``` Каталог включает config-less режим. После объединения настроек `mode` обязателен. +`input?: string | string[]` по умолчанию равен `./icons`. Каждое значение задаёт папку, точный SVG-файл или glob и считается от каталога конфига либо config-less source-каталога. Папка сканируется плоско; рекурсия включается только явным glob, например `./icons/**/*.svg`. Массив объединяет источники, а элементы с префиксом `!` исключают совпадения. Каждый positive-элемент должен разрешаться хотя бы в один SVG. Итоговые файлы дедуплицируются и сортируются, а разные файлы с одинаковым basename вызывают ошибку. + ## Overrides ```ts await generateSprite('src/ui/icons/custom.json', { mode: 'react@webpack', - inputFiles: ['../../shared/search.svg'], + input: [ + '../../shared/icons/**/*.svg', + '!../../shared/icons/legacy-*.svg', + ], transform: { addTransition: false }, }) ``` -Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `inputFiles` заменяет массив из config. +Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `input` заменяет значение из config. Специализированные `generateReactSprite` и `generateNextSprite` оставлены как совместимые обёртки, но для нового кода предпочитай `generateSprite`. diff --git a/skills/svg-sprites/src/ru/references/react-vite.md b/skills/svg-sprites/src/ru/references/react-vite.md deleted file mode 100644 index 60812d9..0000000 --- a/skills/svg-sprites/src/ru/references/react-vite.md +++ /dev/null @@ -1,166 +0,0 @@ -# React с Vite: операционный reference - -## Когда открывать - -Открывай этот документ, если проект использует React без Next.js и собирается Vite, либо если generated-компонент содержит импорт `sprite.svg?no-inline`. Не применяй этот target к Webpack: для него открой [react-webpack.md](react-webpack.md). - -## Сначала установи контекст - -1. Проверь `package.json`: должны быть React, Vite и фактические команды `dev`, `build`, `typecheck`. -2. Найди существующие `svg-sprite.config.ts` и scripts с `svg-sprites`. Не создавай второй каталог для уже существующего спрайта. -3. Выбери целевой каталог для конкретного спрайта. Это не обязан быть каталог module или feature: генератор принимает каталог с config, а не путь к самому config или `icons/`. -4. Не редактируй вручную `.svg-sprite` и локальный `.gitignore`: ими владеет генератор. Корневой `index.ts` принадлежит приложению. - -Минимальная структура: - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -## Настройка - -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -`src/ui/file-manager/svg-sprite/svg-sprite.config.ts`: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -- Каждый такой config описывает один конкретный спрайт; в приложении может быть много независимых config и спрайтов. -- Все пути в конфиге разрешаются относительно каталога `svg-sprite.config.ts`. -- `inputFolder` по умолчанию равен `./icons`; сканирование папки не рекурсивно и включает файлы с окончанием `.svg`. -- `inputFolder` и `inputFiles` объединяются, одинаковый абсолютный путь дедуплицируется. -- Если `inputFiles` заполнен, а неявного `./icons` нет, генерация работает только по списку. Явно заданная отсутствующая `inputFolder` всегда является ошибкой. -- Разные файлы с одинаковым basename, например два `check.svg`, конфликтуют как одно публичное имя иконки. -- `name` должен быть kebab-case и начинаться с латинской буквы. `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames` ниже — только пример generated-имён для `name: 'file-manager'`. -- React preset всегда создаёт формат `stack`; выбрать `symbol` здесь нельзя. - -## Команда и scripts - -Добавь локальный CLI в `package.json` и запускай до процессов, которым нужны generated imports: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Если в проекте уже есть `predev` или `prebuild`, объедини команды в существующем orchestration вместо перезаписи script. - -Для первой генерации запусти `npm run sprite:file-manager`. - -## Использование - -Импортируй только публичный локальный entry: - -```tsx -import { FileManagerIcon, fileManagerIconNames } from './svg-sprite' -import type { FileManagerIconName, FileManagerIconStyle } from './svg-sprite' - -const colorStyle: FileManagerIconStyle = { - '--icon-color-1': '#2563eb', -} - -export function FolderIcon({ icon }: { icon: FileManagerIconName }) { - return -} - -export const availableIcons = fileManagerIconNames -``` - -`width` и `height` в JSX необязательны: размер можно задать CSS-классом. Без `wrapped` компонент рендерит `` и принимает SVG attributes. С `wrapped={true}` корнем становится ``, а внутренний SVG занимает его ширину и высоту: - -```tsx - -``` - -Для обычного импорта используй пользовательский barrel с `export * from './.svg-sprite'`; остальные файлы скрытого каталога считаются внутренними. - -## Нюанс target - -Vite target генерирует статический импорт: - -```ts -import spriteUrl from './sprite.svg?no-inline' -``` - -Query `?no-inline` обязателен: он не даёт Vite превратить небольшой SVG в data URL. Не удаляй query и не копируй generated SVG в `public`; Vite должен выпустить отдельный asset с content hash. - -Для низкоуровневого `` применяй тот же механизм: - -```tsx -import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline' - - - - -``` - -Ручной fragment `#check` безопасен только для имён вида `^[a-zA-Z][a-zA-Z0-9_-]*$`. Для пробелов и других символов generated-компонент использует стабильный hash ID; точный ID находится в `.svg-sprite/svg-sprite.manifest.js`. - -## SpriteViewer - -После генерации добавь Viewer только на debug-маршрут: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js', -) - -export function IconsDebugPage() { - return -} -``` - -Аргумент `import.meta.glob` должен оставаться строковым литералом. Генерация обязана завершиться до старта Vite, иначе новые manifests не попадут в glob. - -## Проверка результата - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -Это быстрые обязательные проверки. Проверь generated-файлы статически: - -- пользовательский `index.ts` переэкспортирует компонент, props, style, union имени и runtime-массив из `.svg-sprite`; -- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "vite"`, `format: "stack"` и ожидаемое число иконок; - -Если менялись target, asset pipeline или диагностируется runtime, дополнительно запусти production build. При наличии браузерных инструментов проверь Network: отдельный `.svg` asset вместо `data:image/svg+xml`, успешный URL и ``. Для сложных цветов, `defs` и размеров следуй [complex-svg.md](complex-svg.md), не утверждая визуальный или a11y результат без доступных инструментов. - -## Типовые ошибки - -- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу. -- `Sprite mode is required`: добавь `mode: 'react@vite'` в config либо передай `--mode react@vite`. -- Иконки нет в autocomplete: проверь case-sensitive окончание `.svg`, нерекурсивное расположение и повторно запусти генерацию до typecheck. -- `Refusing to overwrite a user file`: не удаляй marker и не обходи writer; перенеси пользовательский файл или выбери другой sprite-каталог. -- Viewer пуст: проверь строковый glob, существование `.svg-sprite/svg-sprite.manifest.js` и порядок запуска `predev`. -- SVG оказался inline: проверь, что модуль сгенерирован target `vite` и импорт сохранил `?no-inline`. -- TypeScript не разрешает package subpath: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -Для запуска без CLI используй [programmatic-api.md](programmatic-api.md). diff --git a/skills/svg-sprites/src/ru/references/react-webpack.md b/skills/svg-sprites/src/ru/references/react-webpack.md deleted file mode 100644 index 601b1c2..0000000 --- a/skills/svg-sprites/src/ru/references/react-webpack.md +++ /dev/null @@ -1,152 +0,0 @@ -# React с Webpack 5: операционный reference - -## Когда открывать - -Открывай этот документ для React-приложения на Webpack 5, когда SVG должен проходить через Asset Modules. Если проект собирается Vite, используй [react-vite.md](react-vite.md). Для Next.js не выбирай `react@webpack`: router-specific targets описаны в [next-app.md](next-app.md) и [next-pages.md](next-pages.md). - -## Диагностика окружения до изменений - -1. Подтверди Webpack major version 5 в `package.json` или lockfile. -2. Изучи `module.rules` для `.svg`, `output.publicPath`, dev-server и существующие asset conventions. -3. Найди существующие `svg-sprite.config.ts` и scripts генерации. -4. Выбери каталог проекта конкретного спрайта. Он не обязан совпадать с module или feature-каталогом. Команда принимает каталог с config, не файл config и не папку иконок. - -Минимальная структура: - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -## Конфигурация и генерация - -Установи пакет как development dependency: - -```bash -npm install --save-dev @gromlab/svg-sprites -``` - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@webpack', - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: ['../../../../shared/icons/check.svg'], -}) -``` - -Каждый config описывает один из потенциально многих независимых спрайтов приложения. - -Пути считаются от `svg-sprite.config.ts`. Папка сканируется только на первом уровне. `inputFolder` и `inputFiles` объединяются; одинаковый путь дедуплицируется, но одинаковые basename у разных файлов вызывают конфликт ID. Неявный `./icons` можно не создавать, если заполнен `inputFiles`; явно заданная отсутствующая папка является ошибкой. `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`. - -Рекомендуемые lifecycle hooks: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Не затирай уже существующие pre-scripts: включи генерацию в их текущую цепочку. Для первой генерации запусти `npm run sprite:file-manager`. React preset всегда выпускает `stack`. - -## Публичный компонент - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export function OpenFolderButton() { - return ( - - ) -} -``` - -`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Пользовательский barrel также экспортирует `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName` и `fileManagerIconNames`. Не редактируй `.svg-sprite` или созданный `.gitignore`. - -## Нюанс Webpack target - -Generated-компонент получает URL только через статическое выражение: - -```ts -const spriteUrl = new URL('./sprite.svg', import.meta.url).href -``` - -Webpack 5 распознаёт его как Asset Module и заменяет на публичный URL отдельного SVG. Для корректной обработки: - -- не оборачивай путь в переменную и не меняй generated expression; -- убедись, что Babel/TypeScript не преобразует `import.meta.url` до Webpack; -- не позволяй `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общему SVG rule перехватить `svg-sprite/.svg-sprite/sprite.svg`; -- при custom rule либо исключи generated sprite из component/raw loader, либо добавь отдельное правило `type: 'asset/resource'`; -- проверь `output.publicPath`, особенно при CDN, subpath deployment и dev-server. - -Пример отдельного правила, если существующий loader перехватывает все SVG: - -```js -{ - test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/, - type: 'asset/resource', -} -``` - -Согласуй это правило с текущим конфигом проекта: не добавляй дублирующий matcher, если стандартные Asset Modules уже обрабатывают `new URL` правильно. - -## SpriteViewer - -Webpack не предоставляет `import.meta.glob`. Передай статические lazy imports со строковыми литералами: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export function IconsDebugPage() { - return -} -``` - -Динамически собранный путь `import(path)` не подходит: Webpack не сможет точно связать manifests и assets. Viewer должен быть доступен только на debug/internal route. - -## Проверка - -```bash -npm run sprite:file-manager -npm run typecheck -``` - -Это быстрые обязательные проверки. После генерации проверь: - -- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "webpack"` и `format: "stack"`; -- generated component содержит `new URL('./sprite.svg', import.meta.url).href`; - -Production build и браузер/Network нужны дополнительно, если менялись target, Webpack asset rules, `publicPath`/deployment pipeline или диагностируется runtime. Тогда проверь отдельный hashed SVG asset, HTTP(S)-URL `.svg#id`, content type, fragment ID и корректный CDN/subpath URL. Не утверждай визуальную или a11y корректность без доступных инструментов. - -Для иконок с gradients, filters, masks или внутренним CSS выполни проверки из [complex-svg.md](complex-svg.md). - -## Типовые ошибки - -- `Unsupported React target`: программному API передано не `'webpack'`; CLI mode должен быть ровно `react@webpack`. -- Webpack пытается отрендерить SVG как React-компонент: generated sprite попал под SVGR rule; исключи его или приоритизируй `asset/resource`. -- URL ведёт на неверный host/subpath: исправь `output.publicPath` и настройки runtime deployment, не generated-файл. -- `import.meta` не поддержан: проверь, что сборка действительно Webpack 5 и промежуточный transpiler сохраняет выражение. -- Viewer не загружает manifest: проверь literal path, chunk loading и наличие генерации до компиляции. -- `Refusing to overwrite a user file`: каталог уже содержит пользовательский `.gitignore` или файл в `.svg-sprite`; перенеси его, не обходи защиту. -- Иконка не меняет цвет: используй `color` для монохромной либо `--icon-color-N` через ``; CSS страницы не проникает внутрь ``. - -Для custom build orchestration см. [programmatic-api.md](programmatic-api.md). diff --git a/src/cli/parse-args.ts b/src/cli/parse-args.ts index b74a276..c6b57be 100644 --- a/src/cli/parse-args.ts +++ b/src/cli/parse-args.ts @@ -25,8 +25,7 @@ export const CLI_USAGE = [ ' --mode ', ' --name ', ' --description ', - ' --input-folder ', - ' --input-file Repeat for multiple files', + ' --input Repeat for multiple inputs', ' --[no-]remove-size', ' --[no-]replace-colors', ' --[no-]add-transition', @@ -86,15 +85,12 @@ export function parseCliArgs(argv: string[]): CliArgs | { help: true } { index = nextIndex continue } - if (argument === '--input-folder' || argument.startsWith('--input-folder=')) { - const [value, nextIndex] = optionValue(argv, index, '--input-folder') - overrides.inputFolder = value - index = nextIndex - continue - } - if (argument === '--input-file' || argument.startsWith('--input-file=')) { - const [value, nextIndex] = optionValue(argv, index, '--input-file') - overrides.inputFiles = [...(overrides.inputFiles ?? []), value] + if (argument === '--input' || argument.startsWith('--input=')) { + const [value, nextIndex] = optionValue(argv, index, '--input') + const input = overrides.input === undefined + ? [] + : Array.isArray(overrides.input) ? overrides.input : [overrides.input] + overrides.input = [...input, value] index = nextIndex continue } diff --git a/src/config.ts b/src/config.ts index 30940b1..bbf3fc2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,8 +10,7 @@ const CONFIG_FIELDS = new Set([ 'mode', 'name', 'description', - 'inputFolder', - 'inputFiles', + 'input', 'transform', 'generatedNotice', ]) @@ -64,6 +63,9 @@ export function validateSpriteConfig(value: unknown): asserts value is SpriteCon if ('output' in config || 'sprites' in config || 'preview' in config) { throw configError('legacy config fields are no longer supported.') } + if ('inputFolder' in config || 'inputFiles' in config) { + throw configError('"inputFolder" and "inputFiles" are no longer supported. Use "input".') + } for (const field of Object.keys(config)) { if (!CONFIG_FIELDS.has(field)) throw configError(`unknown field "${field}".`) } @@ -76,19 +78,14 @@ export function validateSpriteConfig(value: unknown): asserts value is SpriteCon if (config.description !== undefined && typeof config.description !== 'string') { throw configError('"description" must be a string.') } - if ('icons' in config) { - throw configError('"icons" was renamed to "inputFolder".') - } - if (config.inputFolder !== undefined && ( - typeof config.inputFolder !== 'string' || config.inputFolder.trim() === '' + if (config.input !== undefined && ( + typeof config.input !== 'string' + ? !Array.isArray(config.input) + || config.input.length === 0 + || config.input.some((entry) => typeof entry !== 'string' || entry.trim() === '') + : config.input.trim() === '' )) { - throw configError('"inputFolder" must be a non-empty string.') - } - if (config.inputFiles !== undefined && ( - !Array.isArray(config.inputFiles) - || config.inputFiles.some((filePath) => typeof filePath !== 'string' || filePath.trim() === '') - )) { - throw configError('"inputFiles" must be an array of non-empty strings.') + throw configError('"input" must be a non-empty string or an array of non-empty strings.') } if (config.transform !== undefined) { if ( @@ -211,20 +208,20 @@ export function resolveSpriteConfig( const name = merged.name ?? getDefaultName(rootDir) validateSpriteName(name) - const inputFiles = (merged.inputFiles ?? []).map((filePath) => path.resolve(rootDir, filePath)) - const defaultInputFolder = path.resolve(rootDir, 'icons') - const inputFolder = merged.inputFolder === undefined - && inputFiles.length > 0 - && !fs.existsSync(defaultInputFolder) - ? null - : path.resolve(rootDir, merged.inputFolder ?? 'icons') + const configuredInput = merged.input ?? 'icons' + const input = (Array.isArray(configuredInput) ? configuredInput : [configuredInput]) + .map((entry) => { + const negated = entry.startsWith('!') + const resolved = path.resolve(rootDir, negated ? entry.slice(1) : entry) + const normalized = path.sep === '/' ? resolved : resolved.replaceAll(path.sep, '/') + return negated ? `!${normalized}` : normalized + }) return { mode: merged.mode, name, description: merged.description, - inputFolder, - inputFiles, + input, transform: { removeSize: transform.removeSize ?? true, replaceColors: transform.replaceColors ?? true, diff --git a/src/core/mode-adapter.ts b/src/core/mode-adapter.ts index 38af85b..2da8014 100644 --- a/src/core/mode-adapter.ts +++ b/src/core/mode-adapter.ts @@ -42,6 +42,8 @@ export type ModeResultMetadata = ReactModeResultMetadata | NextModeResultMetadat export type OutputPlan = { readonly files: readonly GeneratedFile[] + /** Создавать управляемый `.gitignore` для generated-каталога. По умолчанию: true. */ + readonly createGitignore?: boolean readonly paths: { readonly generatedDir: '.svg-sprite' readonly sprite: string @@ -53,6 +55,5 @@ export type OutputPlan = { export interface ModeAdapter { readonly mode: M - readonly contractVersion: number generate(context: ModeAdapterContext): Promise } diff --git a/src/core/output-writer.ts b/src/core/output-writer.ts index d5bbbe4..3bdfe37 100644 --- a/src/core/output-writer.ts +++ b/src/core/output-writer.ts @@ -2,44 +2,17 @@ import { randomUUID } from 'node:crypto' import fs from 'node:fs' import path from 'node:path' import type { SpriteMode } from '../targets/types.js' -import type { GeneratedFile } from './mode-adapter.js' import { GENERATED_MARKER, GENERATED_NOTICE_MARKER, - GENERATOR, } from './generated-markers.js' +import type { GeneratedFile } from './mode-adapter.js' -const STATE_PATH = '.svg-sprite/state.json' -const PREVIOUS_STATE_PATHS = [ - '.svg-sprite-data/state.json', - 'generated/state.json', -] as const -const LEGACY_STATE_PATH = 'generated/.svg-sprites.manifest.json' const DATA_ROOT = '.svg-sprite' -const PREVIOUS_DATA_ROOT = '.svg-sprite-data' -const SYSTEM_FILES: readonly GeneratedFile[] = [ - { - path: '.gitignore', - content: `# ${GENERATED_MARKER}. Do not edit.\n/${DATA_ROOT}/\n`, - }, -] - -type OutputState = { - schemaVersion: 1 - generator: typeof GENERATOR - owner: { - mode: SpriteMode - contractVersion: number - } - files: string[] - warning?: string -} - -type LegacyState = { - version: 1 - generator: typeof GENERATOR - files: string[] -} +const DATA_PREFIX = `${DATA_ROOT}/` +const BACKUP_SUFFIX = '.old' +const STAGING_SUFFIX = '.tmp' +const GITIGNORE_CONTENT = `# ${GENERATED_MARKER}. Do not edit.\n/${DATA_ROOT}/\n` function normalizeManagedPath(relativePath: string): string { const normalized = relativePath.replaceAll('\\', '/') @@ -49,9 +22,6 @@ function normalizeManagedPath(relativePath: string): string { normalized === '' || path.posix.isAbsolute(normalized) || parts.some((part) => part === '' || part === '.' || part === '..') - || normalized === STATE_PATH - || PREVIOUS_STATE_PATHS.includes(normalized as typeof PREVIOUS_STATE_PATHS[number]) - || normalized === LEGACY_STATE_PATH ) { throw new Error(`Invalid generated file path: ${relativePath}`) } @@ -59,23 +29,11 @@ function normalizeManagedPath(relativePath: string): string { return normalized } -function resolveManagedPath(rootDir: string, relativePath: string): string { - const normalized = normalizeManagedPath(relativePath) - const resolved = path.resolve(rootDir, normalized) - const relative = path.relative(rootDir, resolved) - - if (relative.startsWith('..') || path.isAbsolute(relative)) { - throw new Error(`Invalid generated file path: ${relativePath}`) - } - - return resolved -} - -function resolveInternalPath(rootDir: string, relativePath: string): string { +function resolveInsideRoot(rootDir: string, relativePath: string): string { const resolved = path.resolve(rootDir, relativePath) const relative = path.relative(rootDir, resolved) if (relative.startsWith('..') || path.isAbsolute(relative)) { - throw new Error(`Invalid internal output path: ${relativePath}`) + throw new Error(`Invalid generated path: ${relativePath}`) } return resolved } @@ -97,115 +55,8 @@ function assertNoSymlinks(rootDir: string, filePath: string): void { } } -function parseJsonFile(filePath: string): unknown { - try { - return JSON.parse(fs.readFileSync(filePath, 'utf-8')) - } catch { - throw new Error(`Cannot parse generated state: ${filePath}`) - } -} - -function readOutputState(statePath: string): OutputState { - const state = parseJsonFile(statePath) - if ( - !state - || typeof state !== 'object' - || !('schemaVersion' in state) - || state.schemaVersion !== 1 - || !('generator' in state) - || state.generator !== GENERATOR - || !('owner' in state) - || !state.owner - || typeof state.owner !== 'object' - || !('mode' in state.owner) - || typeof state.owner.mode !== 'string' - || !('contractVersion' in state.owner) - || typeof state.owner.contractVersion !== 'number' - || !('files' in state) - || !Array.isArray(state.files) - || !state.files.every((file) => typeof file === 'string') - ) { - throw new Error(`Invalid generated state: ${statePath}`) - } - return state as OutputState -} - -function readPreviousFiles(rootDir: string): { - files: string[] - obsoleteStatePath: string | null -} { - for (const relativeStatePath of [STATE_PATH, ...PREVIOUS_STATE_PATHS]) { - const statePath = resolveInternalPath(rootDir, relativeStatePath) - assertNoSymlinks(rootDir, statePath) - if (!fs.existsSync(statePath)) continue - return { - files: readOutputState(statePath).files.map(normalizeManagedPath), - obsoleteStatePath: relativeStatePath === STATE_PATH ? null : statePath, - } - } - - const legacyStatePath = resolveInternalPath(rootDir, LEGACY_STATE_PATH) - assertNoSymlinks(rootDir, legacyStatePath) - if (!fs.existsSync(legacyStatePath)) return { files: [], obsoleteStatePath: null } - - const legacyState = parseJsonFile(legacyStatePath) - if ( - !legacyState - || typeof legacyState !== 'object' - || !('version' in legacyState) - || legacyState.version !== 1 - || !('generator' in legacyState) - || legacyState.generator !== GENERATOR - || !('files' in legacyState) - || !Array.isArray(legacyState.files) - || !legacyState.files.every((file) => typeof file === 'string') - ) { - throw new Error(`Invalid generated state: ${legacyStatePath}`) - } - - return { - files: (legacyState as LegacyState).files.map(normalizeManagedPath), - obsoleteStatePath: legacyStatePath, - } -} - -function removeEmptyStateDirectory(rootDir: string, statePath: string): void { - const directory = path.dirname(statePath) - if (directory === rootDir) return - try { - fs.rmdirSync(directory) - } catch (error) { - if (error instanceof Error && 'code' in error && ( - error.code === 'ENOENT' || error.code === 'ENOTEMPTY' - )) return - throw error - } -} - -function removeEmptyManagedParents(rootDir: string, filePath: string): void { - for (const relativeDataRoot of [DATA_ROOT, PREVIOUS_DATA_ROOT]) { - const dataRoot = resolveInternalPath(rootDir, relativeDataRoot) - let directory = path.dirname(filePath) - const relative = path.relative(dataRoot, directory) - if (relative.startsWith('..') || path.isAbsolute(relative)) continue - - while (directory !== dataRoot) { - try { - fs.rmdirSync(directory) - } catch (error) { - if (error instanceof Error && 'code' in error && ( - error.code === 'ENOENT' || error.code === 'ENOTEMPTY' - )) return - throw error - } - directory = path.dirname(directory) - } - return - } -} - function hasGeneratedMarker(filePath: string): boolean { - if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return false + if (!fs.existsSync(filePath) || !fs.lstatSync(filePath).isFile()) return false const content = fs.readFileSync(filePath, 'utf-8') return content.includes(GENERATED_MARKER) || content.includes(GENERATED_NOTICE_MARKER) } @@ -228,70 +79,142 @@ function writeFileAtomic(rootDir: string, filePath: string, content: string | Ui } } -/** Применяет mode output plan и последним записывает ownership state. */ +function validateGitignore(rootDir: string, createGitignore: boolean): void { + if (!createGitignore) return + const gitignorePath = resolveInsideRoot(rootDir, '.gitignore') + assertNoSymlinks(rootDir, gitignorePath) + + if (fs.existsSync(gitignorePath) && !hasGeneratedMarker(gitignorePath)) { + throw new Error( + `Refusing to overwrite a user file: ${gitignorePath}\n` + + 'Move the file or choose another sprite directory.', + ) + } +} + +function updateGitignore(rootDir: string, createGitignore: boolean): void { + const gitignorePath = resolveInsideRoot(rootDir, '.gitignore') + + if (createGitignore) { + writeFileAtomic(rootDir, gitignorePath, GITIGNORE_CONTENT) + } else if (hasGeneratedMarker(gitignorePath)) { + fs.unlinkSync(gitignorePath) + } +} + +function transientPaths(rootDir: string, suffix: string): string[] { + return fs.readdirSync(rootDir) + .filter((entry) => entry.startsWith(`${DATA_ROOT}.`) && entry.endsWith(suffix)) + .map((entry) => resolveInsideRoot(rootDir, entry)) +} + +function recoverInterruptedReplacement(rootDir: string): void { + const outputPath = resolveInsideRoot(rootDir, DATA_ROOT) + assertNoSymlinks(rootDir, outputPath) + + const backups = transientPaths(rootDir, BACKUP_SUFFIX) + .sort((left, right) => fs.lstatSync(right).mtimeMs - fs.lstatSync(left).mtimeMs) + + if (!fs.existsSync(outputPath) && backups.length > 0) { + const [latestBackup] = backups.splice(0, 1) + assertNoSymlinks(rootDir, latestBackup) + fs.renameSync(latestBackup, outputPath) + } + + for (const transientPath of [ + ...backups, + ...transientPaths(rootDir, STAGING_SUFFIX), + ]) { + assertNoSymlinks(rootDir, transientPath) + fs.rmSync(transientPath, { recursive: true, force: true }) + } +} + +function stageOutput(rootDir: string, files: readonly GeneratedFile[]): string { + const stagingPath = resolveInsideRoot( + rootDir, + `${DATA_ROOT}.${process.pid}.${randomUUID()}${STAGING_SUFFIX}`, + ) + assertNoSymlinks(rootDir, stagingPath) + fs.mkdirSync(stagingPath) + + try { + for (const file of files) { + const relativePath = file.path.slice(DATA_PREFIX.length) + const filePath = path.resolve(stagingPath, relativePath) + const relative = path.relative(stagingPath, filePath) + if (relative.startsWith('..') || path.isAbsolute(relative)) { + throw new Error(`Invalid generated file path: ${file.path}`) + } + fs.mkdirSync(path.dirname(filePath), { recursive: true }) + fs.writeFileSync(filePath, file.content, { flag: 'wx' }) + } + return stagingPath + } catch (error) { + fs.rmSync(stagingPath, { recursive: true, force: true }) + throw error + } +} + +function replaceOutput(rootDir: string, stagingPath: string): void { + const outputPath = resolveInsideRoot(rootDir, DATA_ROOT) + const backupPath = resolveInsideRoot( + rootDir, + `${DATA_ROOT}.${process.pid}.${randomUUID()}${BACKUP_SUFFIX}`, + ) + assertNoSymlinks(rootDir, outputPath) + + if (fs.existsSync(outputPath) && !fs.lstatSync(outputPath).isDirectory()) { + throw new Error(`Generated output path must be a directory: ${outputPath}`) + } + + let movedPrevious = false + try { + if (fs.existsSync(outputPath)) { + fs.renameSync(outputPath, backupPath) + movedPrevious = true + } + fs.renameSync(stagingPath, outputPath) + } catch (error) { + if (movedPrevious && !fs.existsSync(outputPath) && fs.existsSync(backupPath)) { + fs.renameSync(backupPath, outputPath) + } + throw error + } + + if (movedPrevious) fs.rmSync(backupPath, { recursive: true, force: true }) +} + +/** Полностью заменяет generated-каталог output plan. */ export function writeOutputPlan( rootDir: string, mode: SpriteMode, - contractVersion: number, files: readonly GeneratedFile[], - generatedNotice: boolean, + createGitignore = true, ): void { - const normalizedFiles = [...SYSTEM_FILES, ...files].map((file) => ({ + const normalizedFiles = files.map((file) => ({ ...file, path: normalizeManagedPath(file.path), })) - const nextFiles = normalizedFiles.map((file) => file.path) + const paths = normalizedFiles.map((file) => file.path) - if (new Set(nextFiles).size !== nextFiles.length) { + if (paths.some((filePath) => !filePath.startsWith(DATA_PREFIX))) { + throw new Error(`Mode "${mode}" produced a file outside ${DATA_ROOT}.`) + } + if (new Set(paths).size !== paths.length) { throw new Error(`Mode "${mode}" produced duplicate generated file paths.`) } - const previous = readPreviousFiles(rootDir) - const obsoleteFiles: string[] = [] + recoverInterruptedReplacement(rootDir) + validateGitignore(rootDir, createGitignore) + const stagingPath = stageOutput(rootDir, normalizedFiles) - for (const file of normalizedFiles) { - const filePath = resolveManagedPath(rootDir, file.path) - assertNoSymlinks(rootDir, filePath) - if (fs.existsSync(filePath) && !hasGeneratedMarker(filePath)) { - throw new Error( - `Refusing to overwrite a user file: ${filePath}\n` - + 'Move the file or choose another sprite directory.', - ) + try { + replaceOutput(rootDir, stagingPath) + updateGitignore(rootDir, createGitignore) + } finally { + if (fs.existsSync(stagingPath)) { + fs.rmSync(stagingPath, { recursive: true, force: true }) } } - - for (const relativePath of previous.files) { - if (nextFiles.includes(relativePath)) continue - const filePath = resolveManagedPath(rootDir, relativePath) - assertNoSymlinks(rootDir, filePath) - - if (fs.existsSync(filePath)) { - if (!hasGeneratedMarker(filePath)) { - throw new Error(`Refusing to delete a user file: ${filePath}`) - } - obsoleteFiles.push(filePath) - } - } - - for (const file of normalizedFiles) { - writeFileAtomic(rootDir, resolveManagedPath(rootDir, file.path), file.content) - } - for (const filePath of obsoleteFiles) fs.unlinkSync(filePath) - for (const filePath of obsoleteFiles) removeEmptyManagedParents(rootDir, filePath) - if (previous.obsoleteStatePath && fs.existsSync(previous.obsoleteStatePath)) { - fs.unlinkSync(previous.obsoleteStatePath) - removeEmptyStateDirectory(rootDir, previous.obsoleteStatePath) - } - - const state: OutputState = { - schemaVersion: 1, - generator: GENERATOR, - owner: { mode, contractVersion }, - files: nextFiles, - ...(generatedNotice && { - warning: 'АВТОМАТИЧЕСКИ СГЕНЕРИРОВАННЫЙ ФАЙЛ. Не редактируйте вручную: изменения будут перезаписаны.', - }), - } - const statePath = resolveInternalPath(rootDir, STATE_PATH) - writeFileAtomic(rootDir, statePath, `${JSON.stringify(state, null, 2)}\n`) } diff --git a/src/core/prepare-sprite.ts b/src/core/prepare-sprite.ts index a63046a..d3be1bf 100644 --- a/src/core/prepare-sprite.ts +++ b/src/core/prepare-sprite.ts @@ -26,8 +26,7 @@ export function prepareSprite(config: ResolvedSpriteConfig): PreparedSprite { const folder = resolveSpriteSources({ name: config.name, format: 'stack', - inputFolder: config.inputFolder, - inputFiles: config.inputFiles, + input: config.input, }) const iconNames = folder.files .map((filePath) => path.basename(filePath, '.svg')) diff --git a/src/generate.ts b/src/generate.ts index ede6935..bcfaf0b 100644 --- a/src/generate.ts +++ b/src/generate.ts @@ -45,9 +45,8 @@ export async function generateSprite( writeOutputPlan( resolvedSource.rootDir, adapter.mode, - adapter.contractVersion, plan.files, - config.generatedNotice, + plan.createGitignore, ) const generatedDir = path.resolve(resolvedSource.rootDir, plan.paths.generatedDir) diff --git a/src/modes/next-app-turbopack/adapter.ts b/src/modes/next-app-turbopack/adapter.ts index c059912..b4a7e5e 100644 --- a/src/modes/next-app-turbopack/adapter.ts +++ b/src/modes/next-app-turbopack/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const nextAppTurbopackAdapter: ModeAdapter<'next@app/turbopack'> = { mode: 'next@app/turbopack', - contractVersion: 5, - async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true, diff --git a/src/modes/next-app-turbopack/output.ts b/src/modes/next-app-turbopack/output.ts index 7bea198..0da51d8 100644 --- a/src/modes/next-app-turbopack/output.ts +++ b/src/modes/next-app-turbopack/output.ts @@ -151,7 +151,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact } function manifestTypes(config: ResolvedSpriteConfig): string { - return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'next@app/turbopack'", + " target: 'next@app/turbopack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') } export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { diff --git a/src/modes/next-app-webpack/adapter.ts b/src/modes/next-app-webpack/adapter.ts index 9d20de5..8171c3d 100644 --- a/src/modes/next-app-webpack/adapter.ts +++ b/src/modes/next-app-webpack/adapter.ts @@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js' export const nextAppWebpackAdapter: ModeAdapter<'next@app/webpack'> = { mode: 'next@app/webpack', - contractVersion: 5, async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') diff --git a/src/modes/next-app-webpack/output.ts b/src/modes/next-app-webpack/output.ts index cebb996..4050849 100644 --- a/src/modes/next-app-webpack/output.ts +++ b/src/modes/next-app-webpack/output.ts @@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') } -const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') +const manifestTypes = (config: ResolvedSpriteConfig) => [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'next@app/webpack'", + " target: 'next@app/webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', +].join('\n') export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { return [ diff --git a/src/modes/next-pages-turbopack/adapter.ts b/src/modes/next-pages-turbopack/adapter.ts index 8f02f7e..57dd627 100644 --- a/src/modes/next-pages-turbopack/adapter.ts +++ b/src/modes/next-pages-turbopack/adapter.ts @@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js' export const nextPagesTurbopackAdapter: ModeAdapter<'next@pages/turbopack'> = { mode: 'next@pages/turbopack', - contractVersion: 5, async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') diff --git a/src/modes/next-pages-turbopack/output.ts b/src/modes/next-pages-turbopack/output.ts index 816f987..6ad7da7 100644 --- a/src/modes/next-pages-turbopack/output.ts +++ b/src/modes/next-pages-turbopack/output.ts @@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') } -const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') +const manifestTypes = (config: ResolvedSpriteConfig) => [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'next@pages/turbopack'", + " target: 'next@pages/turbopack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', +].join('\n') export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { return [ diff --git a/src/modes/next-pages-webpack/adapter.ts b/src/modes/next-pages-webpack/adapter.ts index a09142c..d6df8ef 100644 --- a/src/modes/next-pages-webpack/adapter.ts +++ b/src/modes/next-pages-webpack/adapter.ts @@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js' export const nextPagesWebpackAdapter: ModeAdapter<'next@pages/webpack'> = { mode: 'next@pages/webpack', - contractVersion: 5, async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true }) const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack') diff --git a/src/modes/next-pages-webpack/output.ts b/src/modes/next-pages-webpack/output.ts index 431cfeb..0c8bdcf 100644 --- a/src/modes/next-pages-webpack/output.ts +++ b/src/modes/next-pages-webpack/output.ts @@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n') } -const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') +const manifestTypes = (config: ResolvedSpriteConfig) => [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'next@pages/webpack'", + " target: 'next@pages/webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', +].join('\n') export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { return [ diff --git a/src/modes/react-vite/adapter.ts b/src/modes/react-vite/adapter.ts index 976f41a..9279d1e 100644 --- a/src/modes/react-vite/adapter.ts +++ b/src/modes/react-vite/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const reactViteAdapter: ModeAdapter<'react@vite'> = { mode: 'react@vite', - contractVersion: 5, - async generate(context) { const bytes = await compileSpriteContent( context.prepared.folder, diff --git a/src/modes/react-vite/output.ts b/src/modes/react-vite/output.ts index c63a1af..7de94c5 100644 --- a/src/modes/react-vite/output.ts +++ b/src/modes/react-vite/output.ts @@ -241,7 +241,30 @@ function generateManifest(config: ResolvedSpriteConfig, artifact: CompiledSprite function generateManifestDeclarations(config: ResolvedSpriteConfig): string { return [ blockHeader(config.generatedNotice), - "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'react@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', diff --git a/src/modes/react-webpack/adapter.ts b/src/modes/react-webpack/adapter.ts index cebb82e..63a7954 100644 --- a/src/modes/react-webpack/adapter.ts +++ b/src/modes/react-webpack/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const reactWebpackAdapter: ModeAdapter<'react@webpack'> = { mode: 'react@webpack', - contractVersion: 5, - async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: false, diff --git a/src/modes/react-webpack/output.ts b/src/modes/react-webpack/output.ts index 425323e..edf86d3 100644 --- a/src/modes/react-webpack/output.ts +++ b/src/modes/react-webpack/output.ts @@ -168,7 +168,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact } function manifestTypes(config: ResolvedSpriteConfig): string { - return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n') + return [ + header(config.generatedNotice), + '', + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifest = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + ' componentName: string', + " mode: 'react@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' spriteUrl: string', + ' icons: readonly SpriteManifestIcon[]', + '}', + '', + 'export declare const spriteManifest: SpriteManifest', + 'export default spriteManifest', + '', + ].join('\n') } export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] { diff --git a/src/modes/standalone-vite/adapter.ts b/src/modes/standalone-vite/adapter.ts index 31814c5..d3d8625 100644 --- a/src/modes/standalone-vite/adapter.ts +++ b/src/modes/standalone-vite/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const standaloneViteAdapter: ModeAdapter<'standalone@vite'> = { mode: 'standalone@vite', - contractVersion: 1, - async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: false, diff --git a/src/modes/standalone-vite/output.ts b/src/modes/standalone-vite/output.ts index 0fd8e27..04c25c1 100644 --- a/src/modes/standalone-vite/output.ts +++ b/src/modes/standalone-vite/output.ts @@ -68,9 +68,10 @@ function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSprite ].join('\n') } -function index(config: ResolvedSpriteConfig): string { +function index(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { const prefix = camel(config.name) const pascalName = pascal(config.name) + const iconTagName = `${config.name}-icon` return [ header(config.generatedNotice), "import spriteUrl from './sprite.svg?no-inline'", @@ -78,9 +79,92 @@ function index(config: ResolvedSpriteConfig): string { '', `export { ${prefix}IconIds, ${prefix}IconNames }`, `export const ${prefix}SpriteUrl = spriteUrl`, + `export const ${prefix}IconTagName = ${JSON.stringify(iconTagName)}`, + `const ${prefix}IconViewBoxes = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.viewBox])), null, 2)}`, + `const ${prefix}IconElementMarker = Symbol.for('@gromlab/svg-sprites/icon-element')`, + `const ${prefix}IconElementSignature = ${prefix}IconTagName + '|' + ${prefix}SpriteUrl`, + `function has${pascalName}Icon(icon) {`, + ` return Object.prototype.hasOwnProperty.call(${prefix}IconIds, icon)`, + '}', `export function get${pascalName}IconHref(icon) {`, ` return ${prefix}SpriteUrl + '#' + ${prefix}IconIds[icon]`, '}', + `export function define${pascalName}IconElement() {`, + ` if (typeof globalThis.customElements === 'undefined' || typeof globalThis.HTMLElement === 'undefined') return`, + ` const existing = globalThis.customElements.get(${prefix}IconTagName)`, + ' if (existing) {', + ` if (existing[${prefix}IconElementMarker] === ${prefix}IconElementSignature) return`, + ` throw new Error('Cannot define <' + ${prefix}IconTagName + '>: this custom element name is already registered.')`, + ' }', + ` class ${pascalName}IconElement extends globalThis.HTMLElement {`, + " static observedAttributes = ['icon']", + ' constructor() {', + ' super()', + ' const ownerDocument = this.ownerDocument', + " const shadowRoot = this.attachShadow({ mode: 'open' })", + " const style = ownerDocument.createElement('style')", + " style.textContent = ':host{display:inline-block;width:1em;height:1em;vertical-align:-0.125em}:host([hidden]){display:none}svg{display:block;width:100%;height:100%}svg[hidden]{display:none}'", + " this._svg = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg')", + " this._svg.setAttribute('part', 'svg')", + " this._svg.setAttribute('aria-hidden', 'true')", + " this._svg.setAttribute('focusable', 'false')", + " this._svg.setAttribute('hidden', '')", + " this._use = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'use')", + " this._use.setAttribute('part', 'use')", + ' this._svg.append(this._use)', + ' shadowRoot.append(style, this._svg)', + ' }', + ' connectedCallback() {', + " if (Object.prototype.hasOwnProperty.call(this, 'icon')) {", + ' const icon = this.icon', + ' delete this.icon', + ' this.icon = icon', + ' }', + ' this._renderIcon()', + ' }', + ' attributeChangedCallback() {', + ' this._renderIcon()', + ' }', + ' get icon() {', + " const icon = this.getAttribute('icon')", + ` return icon !== null && has${pascalName}Icon(icon) ? icon : null`, + ' }', + ' set icon(icon) {', + ' if (icon === null || icon === undefined) {', + " this.removeAttribute('icon')", + ' return', + ' }', + ` if (!has${pascalName}Icon(icon)) throw new TypeError('Unknown ${config.name} icon: ' + icon)`, + " this.setAttribute('icon', icon)", + ' }', + ' _renderIcon() {', + " const icon = this.getAttribute('icon')", + ' if (icon === null) {', + ' this._clearIcon()', + ' return', + ' }', + ` if (!has${pascalName}Icon(icon)) {`, + ' this._clearIcon()', + ` if (this._invalidIcon !== icon) console.error('<' + ${prefix}IconTagName + '>: unknown icon "' + icon + '".')`, + ' this._invalidIcon = icon', + ' return', + ' }', + ' this._invalidIcon = undefined', + ` const viewBox = ${prefix}IconViewBoxes[icon]`, + " if (viewBox === null) this._svg.removeAttribute('viewBox')", + " else this._svg.setAttribute('viewBox', viewBox)", + ` this._use.setAttribute('href', get${pascalName}IconHref(icon))`, + " this._svg.removeAttribute('hidden')", + ' }', + ' _clearIcon() {', + " this._use.removeAttribute('href')", + " this._svg.removeAttribute('viewBox')", + " this._svg.setAttribute('hidden', '')", + ' }', + ' }', + ` Object.defineProperty(${pascalName}IconElement, ${prefix}IconElementMarker, { value: ${prefix}IconElementSignature })`, + ` globalThis.customElements.define(${prefix}IconTagName, ${pascalName}IconElement)`, + '}', '', ].join('\n') } @@ -88,6 +172,7 @@ function index(config: ResolvedSpriteConfig): string { function indexDeclarations(config: ResolvedSpriteConfig): string { const prefix = camel(config.name) const pascalName = pascal(config.name) + const iconTagName = `${config.name}-icon` return [ header(config.generatedNotice), `import type { ${pascalName}IconName } from './icon-data.js'`, @@ -95,7 +180,17 @@ function indexDeclarations(config: ResolvedSpriteConfig): string { `export { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`, `export type { ${pascalName}IconName } from './icon-data.js'`, `export declare const ${prefix}SpriteUrl: string`, + `export declare const ${prefix}IconTagName: ${JSON.stringify(iconTagName)}`, `export declare function get${pascalName}IconHref(icon: ${pascalName}IconName): string`, + `export interface ${pascalName}IconElement extends HTMLElement {`, + ` icon: ${pascalName}IconName | null`, + '}', + `export declare function define${pascalName}IconElement(): void`, + 'declare global {', + ' interface HTMLElementTagNameMap {', + ` ${JSON.stringify(iconTagName)}: ${pascalName}IconElement`, + ' }', + '}', '', ].join('\n') } @@ -133,11 +228,33 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact function manifestTypes(config: ResolvedSpriteConfig): string { return [ header(config.generatedNotice), - "import type { StandaloneSpriteManifest, StandaloneSpriteManifestData } from '@gromlab/svg-sprites'", '', - "export declare const spriteManifestData: StandaloneSpriteManifestData<'standalone@vite'>", - "export declare function createSpriteManifest(spriteUrl: string): StandaloneSpriteManifest<'standalone@vite'>", - "export declare const spriteManifest: StandaloneSpriteManifest<'standalone@vite'>", + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifestData = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " mode: 'standalone@vite'", + " target: 'vite'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export type SpriteManifest = SpriteManifestData & { spriteUrl: string }', + '', + 'export declare const spriteManifestData: SpriteManifestData', + 'export declare function createSpriteManifest(spriteUrl: string): SpriteManifest', + 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', '', ].join('\n') @@ -148,7 +265,7 @@ export function generateOutputFiles( artifact: CompiledSpriteArtifact, ): GeneratedFile[] { return [ - { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config, artifact) }, { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, diff --git a/src/modes/standalone-webpack/adapter.ts b/src/modes/standalone-webpack/adapter.ts index a0f245b..6742c8d 100644 --- a/src/modes/standalone-webpack/adapter.ts +++ b/src/modes/standalone-webpack/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const standaloneWebpackAdapter: ModeAdapter<'standalone@webpack'> = { mode: 'standalone@webpack', - contractVersion: 1, - async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: false, diff --git a/src/modes/standalone-webpack/output.ts b/src/modes/standalone-webpack/output.ts index a00afd0..ef15f6a 100644 --- a/src/modes/standalone-webpack/output.ts +++ b/src/modes/standalone-webpack/output.ts @@ -68,18 +68,102 @@ function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSprite ].join('\n') } -function index(config: ResolvedSpriteConfig): string { +function index(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string { const prefix = camel(config.name) const pascalName = pascal(config.name) + const iconTagName = `${config.name}-icon` return [ header(config.generatedNotice), `import { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`, '', `export { ${prefix}IconIds, ${prefix}IconNames }`, `export const ${prefix}SpriteUrl = new URL('./sprite.svg', import.meta.url).href`, + `export const ${prefix}IconTagName = ${JSON.stringify(iconTagName)}`, + `const ${prefix}IconViewBoxes = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.viewBox])), null, 2)}`, + `const ${prefix}IconElementMarker = Symbol.for('@gromlab/svg-sprites/icon-element')`, + `const ${prefix}IconElementSignature = ${prefix}IconTagName + '|' + ${prefix}SpriteUrl`, + `function has${pascalName}Icon(icon) {`, + ` return Object.prototype.hasOwnProperty.call(${prefix}IconIds, icon)`, + '}', `export function get${pascalName}IconHref(icon) {`, ` return ${prefix}SpriteUrl + '#' + ${prefix}IconIds[icon]`, '}', + `export function define${pascalName}IconElement() {`, + ` if (typeof globalThis.customElements === 'undefined' || typeof globalThis.HTMLElement === 'undefined') return`, + ` const existing = globalThis.customElements.get(${prefix}IconTagName)`, + ' if (existing) {', + ` if (existing[${prefix}IconElementMarker] === ${prefix}IconElementSignature) return`, + ` throw new Error('Cannot define <' + ${prefix}IconTagName + '>: this custom element name is already registered.')`, + ' }', + ` class ${pascalName}IconElement extends globalThis.HTMLElement {`, + " static observedAttributes = ['icon']", + ' constructor() {', + ' super()', + ' const ownerDocument = this.ownerDocument', + " const shadowRoot = this.attachShadow({ mode: 'open' })", + " const style = ownerDocument.createElement('style')", + " style.textContent = ':host{display:inline-block;width:1em;height:1em;vertical-align:-0.125em}:host([hidden]){display:none}svg{display:block;width:100%;height:100%}svg[hidden]{display:none}'", + " this._svg = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg')", + " this._svg.setAttribute('part', 'svg')", + " this._svg.setAttribute('aria-hidden', 'true')", + " this._svg.setAttribute('focusable', 'false')", + " this._svg.setAttribute('hidden', '')", + " this._use = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'use')", + " this._use.setAttribute('part', 'use')", + ' this._svg.append(this._use)', + ' shadowRoot.append(style, this._svg)', + ' }', + ' connectedCallback() {', + " if (Object.prototype.hasOwnProperty.call(this, 'icon')) {", + ' const icon = this.icon', + ' delete this.icon', + ' this.icon = icon', + ' }', + ' this._renderIcon()', + ' }', + ' attributeChangedCallback() {', + ' this._renderIcon()', + ' }', + ' get icon() {', + " const icon = this.getAttribute('icon')", + ` return icon !== null && has${pascalName}Icon(icon) ? icon : null`, + ' }', + ' set icon(icon) {', + ' if (icon === null || icon === undefined) {', + " this.removeAttribute('icon')", + ' return', + ' }', + ` if (!has${pascalName}Icon(icon)) throw new TypeError('Unknown ${config.name} icon: ' + icon)`, + " this.setAttribute('icon', icon)", + ' }', + ' _renderIcon() {', + " const icon = this.getAttribute('icon')", + ' if (icon === null) {', + ' this._clearIcon()', + ' return', + ' }', + ` if (!has${pascalName}Icon(icon)) {`, + ' this._clearIcon()', + ` if (this._invalidIcon !== icon) console.error('<' + ${prefix}IconTagName + '>: unknown icon "' + icon + '".')`, + ' this._invalidIcon = icon', + ' return', + ' }', + ' this._invalidIcon = undefined', + ` const viewBox = ${prefix}IconViewBoxes[icon]`, + " if (viewBox === null) this._svg.removeAttribute('viewBox')", + " else this._svg.setAttribute('viewBox', viewBox)", + ` this._use.setAttribute('href', get${pascalName}IconHref(icon))`, + " this._svg.removeAttribute('hidden')", + ' }', + ' _clearIcon() {', + " this._use.removeAttribute('href')", + " this._svg.removeAttribute('viewBox')", + " this._svg.setAttribute('hidden', '')", + ' }', + ' }', + ` Object.defineProperty(${pascalName}IconElement, ${prefix}IconElementMarker, { value: ${prefix}IconElementSignature })`, + ` globalThis.customElements.define(${prefix}IconTagName, ${pascalName}IconElement)`, + '}', '', ].join('\n') } @@ -87,6 +171,7 @@ function index(config: ResolvedSpriteConfig): string { function indexDeclarations(config: ResolvedSpriteConfig): string { const prefix = camel(config.name) const pascalName = pascal(config.name) + const iconTagName = `${config.name}-icon` return [ header(config.generatedNotice), `import type { ${pascalName}IconName } from './icon-data.js'`, @@ -94,7 +179,17 @@ function indexDeclarations(config: ResolvedSpriteConfig): string { `export { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`, `export type { ${pascalName}IconName } from './icon-data.js'`, `export declare const ${prefix}SpriteUrl: string`, + `export declare const ${prefix}IconTagName: ${JSON.stringify(iconTagName)}`, `export declare function get${pascalName}IconHref(icon: ${pascalName}IconName): string`, + `export interface ${pascalName}IconElement extends HTMLElement {`, + ` icon: ${pascalName}IconName | null`, + '}', + `export declare function define${pascalName}IconElement(): void`, + 'declare global {', + ' interface HTMLElementTagNameMap {', + ` ${JSON.stringify(iconTagName)}: ${pascalName}IconElement`, + ' }', + '}', '', ].join('\n') } @@ -132,11 +227,33 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact function manifestTypes(config: ResolvedSpriteConfig): string { return [ header(config.generatedNotice), - "import type { StandaloneSpriteManifest, StandaloneSpriteManifestData } from '@gromlab/svg-sprites'", '', - "export declare const spriteManifestData: StandaloneSpriteManifestData<'standalone@webpack'>", - "export declare function createSpriteManifest(spriteUrl: string): StandaloneSpriteManifest<'standalone@webpack'>", - "export declare const spriteManifest: StandaloneSpriteManifest<'standalone@webpack'>", + 'export type SpriteManifestColor = {', + ' variable: `--icon-color-${number}`', + ' fallback: string', + '}', + 'export type SpriteManifestIcon = {', + ' name: string', + ' id: string', + ' viewBox: string | null', + ' colors: readonly SpriteManifestColor[]', + '}', + 'export type SpriteManifestData = {', + ' schemaVersion: 1', + " generator: '@gromlab/svg-sprites'", + ' name: string', + ' description?: string', + " mode: 'standalone@webpack'", + " target: 'webpack'", + " format: 'stack' | 'symbol'", + ' iconCount: number', + ' icons: readonly SpriteManifestIcon[]', + '}', + 'export type SpriteManifest = SpriteManifestData & { spriteUrl: string }', + '', + 'export declare const spriteManifestData: SpriteManifestData', + 'export declare function createSpriteManifest(spriteUrl: string): SpriteManifest', + 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', '', ].join('\n') @@ -147,7 +264,7 @@ export function generateOutputFiles( artifact: CompiledSpriteArtifact, ): GeneratedFile[] { return [ - { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) }, + { path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config, artifact) }, { path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) }, { path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) }, { path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) }, diff --git a/src/modes/standalone/adapter.ts b/src/modes/standalone/adapter.ts index e0fa5f4..981afff 100644 --- a/src/modes/standalone/adapter.ts +++ b/src/modes/standalone/adapter.ts @@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js' export const standaloneAdapter: ModeAdapter<'standalone'> = { mode: 'standalone', - contractVersion: 2, - async generate(context) { const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: false, @@ -15,6 +13,7 @@ export const standaloneAdapter: ModeAdapter<'standalone'> = { return { files: generateOutputFiles(context.config, artifact), + createGitignore: false, paths: { generatedDir: '.svg-sprite', sprite: '.svg-sprite/sprite.svg', diff --git a/src/scanner.ts b/src/scanner.ts index 055cdcf..a2b8336 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -1,54 +1,91 @@ import fs from 'node:fs' import path from 'node:path' +import { + convertPathToPattern, + globSync, + isDynamicPattern, +} from 'tinyglobby' import type { SpriteFolder } from './types.js' -/** - * Сканирует папку и возвращает отсортированные абсолютные пути к SVG-файлам. - */ -function scanDirectory(dirPath: string): string[] { - if (!fs.existsSync(dirPath)) { - throw new Error(`Input directory does not exist: ${dirPath}`) - } - - return fs - .readdirSync(dirPath) - .filter((file) => file.endsWith('.svg')) - .sort() - .map((file) => path.join(dirPath, file)) +type PositiveInput = { + source: string + pattern: string + directory: string | null } -/** - * Резолвит массив путей к SVG-файлам в абсолютные пути. - * Проверяет существование каждого файла. - */ -function resolveFiles(files: string[]): string[] { - return files.map((filePath) => { - const resolved = path.resolve(filePath) +function resolvePositiveInput(source: string): PositiveInput { + if (!fs.existsSync(source)) { + if (isDynamicPattern(source)) return { source, pattern: source, directory: null } + throw new Error(`Input path does not exist: ${source}`) + } - if (!fs.existsSync(resolved)) { - throw new Error(`SVG file does not exist: ${resolved}`) + const stats = fs.statSync(source) + if (stats.isDirectory()) { + return { + source, + pattern: `${convertPathToPattern(source)}/*.svg`, + directory: source, } + } + if (!stats.isFile()) { + throw new Error(`Input path must be a file, directory, or glob pattern: ${source}`) + } + if (!source.endsWith('.svg')) { + throw new Error(`File is not an SVG: ${source}`) + } - if (!resolved.endsWith('.svg')) { - throw new Error(`File is not an SVG: ${resolved}`) - } + return { source, pattern: convertPathToPattern(source), directory: null } +} - return resolved - }) +function resolveExclusion(source: string): string { + const target = source.slice(1) + if (!fs.existsSync(target)) return source + + const stats = fs.statSync(target) + if (stats.isDirectory()) return `!${convertPathToPattern(target)}/**` + if (stats.isFile()) return `!${convertPathToPattern(target)}` + return source } type ResolveSpriteSourcesOptions = { name: string format: SpriteFolder['format'] - inputFolder: string | null - inputFiles: string[] + input: string[] } -/** Объединяет SVG из папки и явного списка в один источник спрайта. */ +const GLOB_OPTIONS = { + absolute: true, + expandDirectories: false, + onlyFiles: true, +} as const + +/** Разрешает пути, папки и glob-шаблоны в один набор исходных SVG. */ export function resolveSpriteSources(options: ResolveSpriteSourcesOptions): SpriteFolder { - const { name, format, inputFolder, inputFiles } = options - const folderFiles = inputFolder === null ? [] : scanDirectory(inputFolder) - const files = [...new Set([...folderFiles, ...resolveFiles(inputFiles)])] + const { name, format, input } = options + const positives = input + .filter((entry) => !entry.startsWith('!')) + .map(resolvePositiveInput) + const exclusions = input + .filter((entry) => entry.startsWith('!')) + .map(resolveExclusion) + + if (positives.length === 0) { + throw new Error('Sprite input must include at least one path or positive glob pattern.') + } + + for (const { source, pattern } of positives) { + const matchedSvg = globSync(pattern, GLOB_OPTIONS).some((filePath) => filePath.endsWith('.svg')) + if (!matchedSvg) throw new Error(`Input matched no SVG files: ${source}`) + } + + const files = [...new Set( + globSync([ + ...positives.map(({ pattern }) => pattern), + ...exclusions, + ], GLOB_OPTIONS) + .filter((filePath) => filePath.endsWith('.svg')) + .map((filePath) => path.resolve(filePath)), + )].sort() if (files.length === 0) { throw new Error(`Sprite "${name}" has no SVG files in configured inputs.`) @@ -57,7 +94,7 @@ export function resolveSpriteSources(options: ResolveSpriteSourcesOptions): Spri return { name, format, - path: inputFolder, + path: input.length === 1 ? positives[0].directory : null, files, } } diff --git a/src/types.ts b/src/types.ts index f98ab9f..8aa06a7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -32,10 +32,8 @@ export type SpriteConfig = { name?: string /** Описание спрайта для generated types и manifest. */ description?: string - /** Папка с исходными SVG относительно корня модуля. По умолчанию: ./icons. */ - inputFolder?: string - /** Дополнительные SVG-файлы относительно корня модуля. По умолчанию: []. */ - inputFiles?: string[] + /** Путь или glob-шаблоны исходных SVG относительно корня модуля. По умолчанию: ./icons. */ + input?: string | string[] /** Настройки трансформации SVG. По умолчанию все включены. */ transform?: TransformOptions /** Добавлять развёрнутое предупреждение в generated-файлы. По умолчанию: true. */ @@ -47,8 +45,7 @@ export type ResolvedSpriteConfig = { mode: SpriteMode name: string description?: string - inputFolder: string | null - inputFiles: string[] + input: string[] transform: Required generatedNotice: boolean } @@ -59,7 +56,7 @@ export type SpriteFolder = { name: string /** Формат спрайта. */ format: SpriteFormat - /** Абсолютный путь к папке (для input-папки) или null (для input-массива). */ + /** Абсолютный путь для единственного input-каталога, иначе null. */ path: string | null /** Абсолютные пути к SVG-файлам. */ files: string[] diff --git a/test/cli-input.test.mjs b/test/cli-input.test.mjs new file mode 100644 index 0000000..d138908 --- /dev/null +++ b/test/cli-input.test.mjs @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' + +test('CLI accepts repeated --input paths and glob patterns', () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-cli-input-')) + const iconsDir = path.join(rootDir, 'icons') + fs.mkdirSync(iconsDir) + fs.writeFileSync(path.join(iconsDir, 'check.svg'), '') + fs.writeFileSync(path.join(iconsDir, 'close.svg'), '') + + const result = spawnSync(process.execPath, [ + 'dist/cli.js', + '--mode=standalone', + '--name=cli-icons', + '--input', + 'icons/*.svg', + '--input', + '!icons/close.svg', + rootDir, + ], { + cwd: path.resolve('.'), + encoding: 'utf8', + }) + + assert.equal(result.status, 0, result.stderr) + const sprite = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'sprite.svg'), 'utf8') + assert.match(sprite, /id="check"/) + assert.doesNotMatch(sprite, /id="close"/) +}) + +test('CLI rejects removed input flags', () => { + const result = spawnSync(process.execPath, [ + 'dist/cli.js', + '--input-folder=icons', + '.', + ], { + cwd: path.resolve('.'), + encoding: 'utf8', + }) + + assert.notEqual(result.status, 0) + assert.match(result.stderr, /Unknown argument: --input-folder=icons/) +}) diff --git a/test/documentation-guides.test.mjs b/test/documentation-guides.test.mjs new file mode 100644 index 0000000..e23c37f --- /dev/null +++ b/test/documentation-guides.test.mjs @@ -0,0 +1,76 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +import test from 'node:test' + +const rootDir = path.resolve(import.meta.dirname, '..') +const docsDir = path.join(rootDir, 'docs') +const guideModes = new Map([ + ['standalone.md', 'standalone'], + ['standalone-vite.md', 'standalone@vite'], + ['standalone-webpack.md', 'standalone@webpack'], + ['react-vite.md', 'react@vite'], + ['react-webpack.md', 'react@webpack'], + ['next-app-turbopack.md', 'next@app/turbopack'], + ['next-app-webpack.md', 'next@app/webpack'], + ['next-pages-turbopack.md', 'next@pages/turbopack'], + ['next-pages-webpack.md', 'next@pages/webpack'], +]) + +const sectionHeadings = { + en: ['Generate the sprite', 'Debug and preview', 'Type the config'], + ru: ['Генерация спрайта', 'Дебаг и превью', 'Типизация конфига'], +} + +function markdownFiles(directory) { + return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const entryPath = path.join(directory, entry.name) + return entry.isDirectory() + ? markdownFiles(entryPath) + : entry.name.endsWith('.md') ? [entryPath] : [] + }) +} + +test('exact-mode guides are reusable by docs and skills', () => { + for (const language of ['en', 'ru']) { + const guidesDir = path.join(docsDir, language, 'guides') + const guideFiles = fs.readdirSync(guidesDir) + .filter((file) => file !== 'README.md') + .sort() + assert.deepEqual(guideFiles, [...guideModes.keys()].sort()) + + for (const [file, mode] of guideModes) { + const source = fs.readFileSync(path.join(guidesDir, file), 'utf8') + assert.equal(source.match(/^# /gm)?.length, 1, `${language}/${file} must have one H1`) + const headings = source.match(/^## .+$/gm)?.map((heading) => ( + heading.replace(/^## (?:\d+\. )?/, '') + )) + assert.deepEqual(headings, sectionHeadings[language]) + assert.match(source, /npx --yes (?:--package=@gromlab\/svg-sprites@latest svg-sprites|@gromlab\/svg-sprites@latest)/) + assert.match(source, /npm install --save-dev @gromlab\/svg-sprites/) + assert.match(source, new RegExp(`mode: '${mode.replaceAll('/', '\\/')}'`)) + assert.doesNotMatch(source, /\]\([^)]+\)/, `${language}/${file} must not depend on its location`) + + const generation = source.indexOf(sectionHeadings[language][0]) + const preview = source.indexOf(sectionHeadings[language][1]) + const typing = source.indexOf(sectionHeadings[language][2]) + assert.ok(generation < preview && preview < typing) + } + } +}) + +test('all local documentation links resolve', () => { + for (const file of markdownFiles(docsDir)) { + const source = fs.readFileSync(file, 'utf8') + for (const match of source.matchAll(/\]\(([^)]+)\)/g)) { + const target = match[1] + if (/^(?:https?:|mailto:|#)/.test(target)) continue + const targetPath = decodeURIComponent(target.split('#')[0]) + assert.equal( + fs.existsSync(path.resolve(path.dirname(file), targetPath)), + true, + `${path.relative(rootDir, file)} links to missing ${target}`, + ) + } + } +}) diff --git a/test/mode-contracts.test.mjs b/test/mode-contracts.test.mjs index 048079b..beb973e 100644 --- a/test/mode-contracts.test.mjs +++ b/test/mode-contracts.test.mjs @@ -14,7 +14,6 @@ const GENERATED_FILES = [ 'index.js', 'react', 'sprite.svg', - 'state.json', 'svg-sprite.manifest.d.ts', 'svg-sprite.manifest.js', ] @@ -73,6 +72,7 @@ test('generates the current isolated React Vite contract', async () => { const componentTypes = fs.readFileSync(path.join(generatedDir, 'react', 'react-component.d.ts'), 'utf8') const iconData = fs.readFileSync(path.join(generatedDir, 'icon-data.js'), 'utf8') const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestTypes = fs.readFileSync(path.join(generatedDir, 'svg-sprite.manifest.d.ts'), 'utf8') assert.match(component, /export const FileManagerIcon/) assert.match(component, /sprite\.svg\?no-inline/) @@ -83,23 +83,29 @@ test('generates the current isolated React Vite contract', async () => { assert.match(manifest, /"mode": "react@vite"/) assert.match(manifest, /"target": "vite"/) assert.match(manifest, /"iconCount": 3/) + assert.match(manifestTypes, /export type SpriteManifest/) + assert.match(manifestTypes, /mode: 'react@vite'/) + assert.doesNotMatch(manifestTypes, /@gromlab\/svg-sprites\/react/) - const state = JSON.parse(fs.readFileSync(path.join(generatedDir, 'state.json'), 'utf8')) - assert.deepEqual(state.owner, { mode: 'react@vite', contractVersion: 5 }) - assert.equal(state.files.includes('.svg-sprite/react/react-component.js'), true) }) test('supports Webpack target and a custom icons directory', async () => { const { rootDir, configPath } = createReactFixture({ name: 'documents', - inputFolder: './assets', + input: './assets', generatedNotice: false, }) fs.renameSync(path.join(rootDir, 'icons'), path.join(rootDir, 'assets')) + fs.mkdirSync(path.join(rootDir, 'assets', 'nested')) + fs.writeFileSync( + path.join(rootDir, 'assets', 'nested', 'ignored.svg'), + '', + ) const result = await generateReactSprite(configPath, 'webpack') const component = fs.readFileSync(path.join(result.generatedDir, 'react', 'react-component.js'), 'utf8') const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestTypes = fs.readFileSync(path.join(result.generatedDir, 'svg-sprite.manifest.d.ts'), 'utf8') assert.equal(result.name, 'documents') assert.equal(result.iconCount, 3) @@ -109,9 +115,11 @@ test('supports Webpack target and a custom icons directory', async () => { assert.match(component, /DocumentsIcon/) assert.match(component, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) assert.match(manifest, /"target": "webpack"/) + assert.match(manifestTypes, /mode: 'react@webpack'/) + assert.doesNotMatch(manifestTypes, /@gromlab\/svg-sprites\/react/) }) -test('merges inputFolder and inputFiles and deduplicates identical paths', async () => { +test('combines input paths and glob patterns and deduplicates identical files', async () => { const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-inputs-')) const rootDir = path.join(temporaryDir, 'svg-sprite') const iconsDir = path.join(rootDir, 'icons') @@ -120,7 +128,7 @@ test('merges inputFolder and inputFiles and deduplicates identical paths', async fs.writeFileSync(path.join(iconsDir, 'local.svg'), '') fs.writeFileSync(sharedIcon, '') const configPath = path.join(rootDir, 'config.ts') - fs.writeFileSync(configPath, `export default { name: 'mixed-inputs', inputFiles: [${JSON.stringify(path.relative(rootDir, sharedIcon))}, ${JSON.stringify(path.relative(rootDir, sharedIcon))}] }`) + fs.writeFileSync(configPath, `export default { name: 'mixed-inputs', input: ['./icons/*.svg', ${JSON.stringify(path.relative(rootDir, sharedIcon))}, ${JSON.stringify(path.relative(rootDir, sharedIcon))}] }`) const result = await generateReactSprite(configPath, 'vite') const sprite = fs.readFileSync(result.spritePath, 'utf8') @@ -130,7 +138,7 @@ test('merges inputFolder and inputFiles and deduplicates identical paths', async assert.match(sprite, /id="shared"/) }) -test('supports inputFiles without the default icons directory', async () => { +test('supports one input file without the default icons directory', async () => { const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-files-')) const rootDir = path.join(temporaryDir, 'svg-sprite') const sharedIcon = path.join(temporaryDir, 'shared', 'only.svg') @@ -138,14 +146,49 @@ test('supports inputFiles without the default icons directory', async () => { fs.mkdirSync(path.dirname(sharedIcon), { recursive: true }) fs.writeFileSync(sharedIcon, '') const configPath = path.join(rootDir, 'config.ts') - fs.writeFileSync(configPath, `export default { inputFiles: [${JSON.stringify(path.relative(rootDir, sharedIcon))}] }`) + fs.writeFileSync(configPath, `export default { input: ${JSON.stringify(path.relative(rootDir, sharedIcon))} }`) const result = await generateReactSprite(configPath, 'webpack') assert.equal(result.iconCount, 1) assert.match(fs.readFileSync(result.spritePath, 'utf8'), /id="only"/) }) -test('rejects duplicate names and explicitly missing input folders', async () => { +test('supports recursive input globs and exclusions', async () => { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-globs-')) + const rootDir = path.join(temporaryDir, 'svg-sprite') + const nestedDir = path.join(rootDir, 'icons', 'nested') + fs.mkdirSync(nestedDir, { recursive: true }) + fs.writeFileSync(path.join(rootDir, 'icons', 'local.svg'), '') + fs.writeFileSync(path.join(nestedDir, 'included.svg'), '') + fs.writeFileSync(path.join(nestedDir, 'draft.svg'), '') + const configPath = path.join(rootDir, 'config.ts') + fs.writeFileSync(configPath, "export default { input: ['icons/**/*.svg', '!icons/**/draft.svg'] }") + + const result = await generateReactSprite(configPath, 'vite') + const sprite = fs.readFileSync(result.spritePath, 'utf8') + assert.equal(result.iconCount, 2) + assert.match(sprite, /id="local"/) + assert.match(sprite, /id="included"/) + assert.doesNotMatch(sprite, /id="draft"/) +}) + +test('treats an existing excluded path as a literal', async () => { + const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-literal-exclusion-')) + const rootDir = path.join(temporaryDir, 'svg-sprite') + const iconsDir = path.join(rootDir, 'icons') + fs.mkdirSync(iconsDir, { recursive: true }) + fs.writeFileSync(path.join(iconsDir, 'icon[1].svg'), '') + fs.writeFileSync(path.join(iconsDir, 'icon1.svg'), '') + const configPath = path.join(rootDir, 'config.ts') + fs.writeFileSync(configPath, "export default { input: ['icons/*.svg', '!icons/icon[1].svg'] }") + + const result = await generateReactSprite(configPath, 'vite') + const sprite = fs.readFileSync(result.spritePath, 'utf8') + assert.equal(result.iconCount, 1) + assert.match(sprite, /id="icon1"/) +}) + +test('rejects invalid, missing, and conflicting inputs', async () => { const temporaryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-react-input-errors-')) const rootDir = path.join(temporaryDir, 'svg-sprite') const first = path.join(temporaryDir, 'one', 'shared.svg') @@ -156,12 +199,24 @@ test('rejects duplicate names and explicitly missing input folders', async () => fs.writeFileSync(first, '') fs.writeFileSync(second, '') const duplicateConfig = path.join(rootDir, 'duplicate.ts') - fs.writeFileSync(duplicateConfig, `export default { inputFiles: [${JSON.stringify(path.relative(rootDir, first))}, ${JSON.stringify(path.relative(rootDir, second))}] }`) + fs.writeFileSync(duplicateConfig, `export default { input: [${JSON.stringify(path.relative(rootDir, first))}, ${JSON.stringify(path.relative(rootDir, second))}] }`) await assert.rejects(generateReactSprite(duplicateConfig, 'vite'), /produce the same SVG id "shared"/) const missingConfig = path.join(rootDir, 'missing.ts') - fs.writeFileSync(missingConfig, `export default { inputFolder: './missing', inputFiles: [${JSON.stringify(path.relative(rootDir, first))}] }`) - await assert.rejects(generateReactSprite(missingConfig, 'vite'), /Input directory does not exist/) + fs.writeFileSync(missingConfig, "export default { input: './missing' }") + await assert.rejects(generateReactSprite(missingConfig, 'vite'), /Input path does not exist/) + + const unmatchedConfig = path.join(rootDir, 'unmatched.ts') + fs.writeFileSync(unmatchedConfig, "export default { input: './missing/*.svg' }") + await assert.rejects(generateReactSprite(unmatchedConfig, 'vite'), /Input matched no SVG files/) + + const emptyConfig = path.join(rootDir, 'empty.ts') + fs.writeFileSync(emptyConfig, 'export default { input: [] }') + await assert.rejects(generateReactSprite(emptyConfig, 'vite'), /"input" must be a non-empty string or an array/) + + const legacyConfig = path.join(rootDir, 'legacy.ts') + fs.writeFileSync(legacyConfig, "export default { inputFolder: './icons' }") + await assert.rejects(generateReactSprite(legacyConfig, 'vite'), /"inputFolder" and "inputFiles" are no longer supported/) }) test('applies transform options to SVG and React styles', async () => { @@ -195,6 +250,7 @@ test('generates every Next.js exact mode without a client boundary', async () => const result = await generateNextSprite(configPath, options) const component = fs.readFileSync(path.join(result.generatedDir, 'react', 'react-component.js'), 'utf8') const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestTypes = fs.readFileSync(path.join(result.generatedDir, 'svg-sprite.manifest.d.ts'), 'utf8') const mode = `next@${options.router}/${options.bundler}` assert.equal(result.target, mode) assert.equal(result.router, options.router) @@ -202,31 +258,33 @@ test('generates every Next.js exact mode without a client boundary', async () => assert.match(component, /new URL\('\.\.\/sprite\.svg', import\.meta\.url\)\.href/) assert.doesNotMatch(component, /['"]use client['"]/) assert.match(manifest, new RegExp(`"target": ${JSON.stringify(mode)}`)) + assert.match(manifestTypes, new RegExp(`mode: ${JSON.stringify(mode).replaceAll('"', "'")}`)) + assert.doesNotMatch(manifestTypes, /@gromlab\/svg-sprites\/react/) } }) -test('writer removes only obsolete managed files and preserves user files', async () => { +test('writer replaces the generated directory and preserves root user files', async () => { const { rootDir, configPath } = createReactFixture() await generateReactSprite(configPath, 'vite') - const statePath = path.join(rootDir, '.svg-sprite', 'state.json') - const state = JSON.parse(fs.readFileSync(statePath, 'utf8')) - state.files.push('.svg-sprite/obsolete.js') - fs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`) fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'obsolete.js'), '/* @generated by @gromlab/svg-sprites. */\n') - fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'notes.md'), 'Keep me\n') + fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'notes.md'), 'Remove me\n') fs.writeFileSync(path.join(rootDir, 'index.ts'), 'export const userCode = true\n') await generateReactSprite(configPath, 'vite') assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'obsolete.js')), false) - assert.equal(fs.readFileSync(path.join(rootDir, '.svg-sprite', 'notes.md'), 'utf8'), 'Keep me\n') + assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'notes.md')), false) assert.equal(fs.readFileSync(path.join(rootDir, 'index.ts'), 'utf8'), 'export const userCode = true\n') }) -test('writer refuses user content and symbolic links in managed paths', async () => { +test('writer replaces a reserved output directory and refuses symbolic links', async () => { const first = createReactFixture() fs.mkdirSync(path.join(first.rootDir, '.svg-sprite')) fs.writeFileSync(path.join(first.rootDir, '.svg-sprite', 'index.js'), 'export const userCode = true\n') - await assert.rejects(generateReactSprite(first.configPath, 'vite'), /Refusing to overwrite a user file/) + await generateReactSprite(first.configPath, 'vite') + assert.doesNotMatch( + fs.readFileSync(path.join(first.rootDir, '.svg-sprite', 'index.js'), 'utf8'), + /userCode/, + ) const second = createReactFixture() const outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-outside-')) @@ -235,6 +293,24 @@ test('writer refuses user content and symbolic links in managed paths', async () assert.deepEqual(fs.readdirSync(outsideDir), []) }) +test('writer recovers an interrupted directory replacement', async () => { + const { rootDir, configPath } = createReactFixture() + await generateReactSprite(configPath, 'vite') + const generatedDir = path.join(rootDir, '.svg-sprite') + const backupDir = path.join(rootDir, '.svg-sprite.interrupted.old') + const stagingDir = path.join(rootDir, '.svg-sprite.interrupted.tmp') + fs.renameSync(generatedDir, backupDir) + fs.mkdirSync(stagingDir) + fs.writeFileSync(path.join(stagingDir, 'partial.js'), 'partial\n') + + await generateReactSprite(configPath, 'vite') + + assert.equal(fs.existsSync(generatedDir), true) + assert.equal(fs.existsSync(backupDir), false) + assert.equal(fs.existsSync(stagingDir), false) + assert.equal(fs.existsSync(path.join(generatedDir, 'partial.js')), false) +}) + test('rejects colliding SVG shape IDs', async () => { const { iconsDir, configPath } = createReactFixture() const id = `icon-${createHash('sha256').update('folder open').digest('hex').slice(0, 16)}` diff --git a/test/standalone-icon-element.test.mjs b/test/standalone-icon-element.test.mjs new file mode 100644 index 0000000..f4b4cb4 --- /dev/null +++ b/test/standalone-icon-element.test.mjs @@ -0,0 +1,104 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import test from 'node:test' +import { pathToFileURL } from 'node:url' + +import { JSDOM } from 'jsdom' + +import { generateSprite } from '../dist/index.js' + +const CHECK_SVG = '' +const UNSAFE_SVG = '' + +test('standalone Webpack icon element renders generated icons without external runtime dependencies', async (context) => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-sprites-icon-element-')) + const iconsDir = path.join(rootDir, 'icons') + fs.mkdirSync(iconsDir) + fs.writeFileSync(path.join(rootDir, 'package.json'), '{"type":"module"}') + fs.writeFileSync(path.join(iconsDir, 'check.svg'), CHECK_SVG) + fs.writeFileSync(path.join(iconsDir, 'folder open.svg'), UNSAFE_SVG) + + await generateSprite(rootDir, { + mode: 'standalone@webpack', + name: 'app-icons', + generatedNotice: false, + }) + + const entryUrl = pathToFileURL(path.join(rootDir, '.svg-sprite', 'index.js')).href + const generated = await import(entryUrl) + assert.equal(generated.appIconsIconTagName, 'app-icons-icon') + assert.doesNotThrow(() => generated.defineAppIconsIconElement()) + + const dom = new JSDOM('', { url: 'https://example.test/' }) + const originals = new Map() + for (const [name, value] of Object.entries({ + HTMLElement: dom.window.HTMLElement, + customElements: dom.window.customElements, + })) { + originals.set(name, Object.getOwnPropertyDescriptor(globalThis, name)) + Object.defineProperty(globalThis, name, { configurable: true, writable: true, value }) + } + + const browserErrors = [] + const originalConsoleError = console.error + console.error = (...values) => browserErrors.push(values.join(' ')) + + context.after(() => { + console.error = originalConsoleError + dom.window.close() + for (const [name, descriptor] of originals) { + if (descriptor) Object.defineProperty(globalThis, name, descriptor) + else delete globalThis[name] + } + }) + + const icon = dom.window.document.createElement('app-icons-icon') + icon.icon = 'folder open' + dom.window.document.body.append(icon) + + generated.defineAppIconsIconElement() + generated.defineAppIconsIconElement() + + assert.equal(icon.icon, 'folder open') + assert.equal(icon.getAttribute('icon'), 'folder open') + assert.equal(icon.shadowRoot.querySelector('svg').getAttribute('viewBox'), '0 0 16 16') + assert.equal( + icon.shadowRoot.querySelector('use').getAttribute('href'), + generated.getAppIconsIconHref('folder open'), + ) + assert.match(icon.shadowRoot.querySelector('use').getAttribute('href'), /#icon-[a-f0-9]{16}$/) + + icon.icon = 'check' + assert.equal(icon.shadowRoot.querySelector('svg').getAttribute('viewBox'), '0 0 24 24') + assert.match(icon.shadowRoot.querySelector('use').getAttribute('href'), /sprite\.svg#check$/) + assert.throws(() => { icon.icon = 'missing' }, /Unknown app-icons icon: missing/) + + icon.setAttribute('icon', 'missing') + assert.equal(icon.icon, null) + assert.equal(icon.shadowRoot.querySelector('use').hasAttribute('href'), false) + assert.equal(icon.shadowRoot.querySelector('svg').hasAttribute('hidden'), true) + assert.deepEqual(browserErrors, [': unknown icon "missing".']) + + const collisionDom = new JSDOM('') + Object.defineProperty(globalThis, 'HTMLElement', { + configurable: true, + writable: true, + value: collisionDom.window.HTMLElement, + }) + Object.defineProperty(globalThis, 'customElements', { + configurable: true, + writable: true, + value: collisionDom.window.customElements, + }) + collisionDom.window.customElements.define( + 'app-icons-icon', + class extends collisionDom.window.HTMLElement {}, + ) + assert.throws( + () => generated.defineAppIconsIconElement(), + /Cannot define : this custom element name is already registered/, + ) + collisionDom.window.close() +}) diff --git a/test/standalone-mode.test.mjs b/test/standalone-mode.test.mjs index d44bf48..77de464 100644 --- a/test/standalone-mode.test.mjs +++ b/test/standalone-mode.test.mjs @@ -44,9 +44,9 @@ test('standalone generates an asset and deployment-neutral JSON manifest', async assert.equal(result.manifestPath, path.join(rootDir, '.svg-sprite', 'svg-sprite.manifest.json')) assert.deepEqual(generatedFiles(rootDir), [ 'sprite.svg', - 'state.json', 'svg-sprite.manifest.json', ]) + assert.equal(fs.existsSync(path.join(rootDir, '.gitignore')), false) const source = fs.readFileSync(result.spritePath, 'utf8') assert.match(source, /@generated by @gromlab\/svg-sprites/) @@ -65,8 +65,20 @@ test('standalone generates an asset and deployment-neutral JSON manifest', async /^icon-[a-f0-9]{16}$/, ) - const state = JSON.parse(fs.readFileSync(path.join(rootDir, '.svg-sprite', 'state.json'), 'utf8')) - assert.deepEqual(state.owner, { mode: 'standalone', contractVersion: 2 }) +}) + +test('standalone preserves a user-owned .gitignore', async () => { + const rootDir = fixture() + const gitignorePath = path.join(rootDir, '.gitignore') + fs.writeFileSync(gitignorePath, '/public/cache/\n') + + await generateSprite(rootDir, { + mode: 'standalone', + name: 'app-icons', + generatedNotice: false, + }) + + assert.equal(fs.readFileSync(gitignorePath, 'utf8'), '/public/cache/\n') }) test('standalone Vite generates a typed facade and resolved manifest', async () => { @@ -78,13 +90,13 @@ test('standalone Vite generates a typed facade and resolved manifest', async () }) assert.equal(result.target, 'vite') + assert.equal(fs.existsSync(path.join(rootDir, '.gitignore')), true) assert.deepEqual(generatedFiles(rootDir), [ 'icon-data.d.ts', 'icon-data.js', 'index.d.ts', 'index.js', 'sprite.svg', - 'state.json', 'svg-sprite.manifest.d.ts', 'svg-sprite.manifest.js', ]) @@ -92,13 +104,26 @@ test('standalone Vite generates a typed facade and resolved manifest', async () const entry = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'index.js'), 'utf8') const declarations = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'index.d.ts'), 'utf8') const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestDeclarations = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'svg-sprite.manifest.d.ts'), 'utf8') assert.match(entry, /sprite\.svg\?no-inline/) assert.match(entry, /export const iconsSpriteUrl = spriteUrl/) + assert.match(entry, /export const iconsIconTagName = "icons-icon"/) assert.match(entry, /export function getIconsIconHref\(icon\)/) + assert.match(entry, /export function defineIconsIconElement\(\)/) + assert.match(entry, /class IconsIconElement extends globalThis\.HTMLElement/) + assert.match(entry, /this\.attachShadow\(\{ mode: 'open' \}\)/) + assert.match(entry, /"folder open": "0 0 16 16"/) assert.match(declarations, /getIconsIconHref\(icon: IconsIconName\): string/) + assert.match(declarations, /export interface IconsIconElement extends HTMLElement/) + assert.match(declarations, /icon: IconsIconName \| null/) + assert.match(declarations, /"icons-icon": IconsIconElement/) assert.match(manifest, /"mode": "standalone@vite"/) assert.match(manifest, /export const spriteManifest = createSpriteManifest\(spriteUrl\)/) - assert.doesNotMatch(entry, /react|jsx|\.css/) + assert.match(manifestDeclarations, /export type SpriteManifestData/) + assert.match(manifestDeclarations, /mode: 'standalone@vite'/) + assert.doesNotMatch(manifestDeclarations, /from '@gromlab\/svg-sprites'/) + assert.doesNotMatch(entry, /react|jsx|\.css|\blit\b/i) + }) test('standalone Webpack generates a typed facade and resolved manifest', async () => { @@ -112,31 +137,39 @@ test('standalone Webpack generates a typed facade and resolved manifest', async assert.equal(result.target, 'webpack') const entry = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'index.js'), 'utf8') const manifest = fs.readFileSync(result.manifestPath, 'utf8') + const manifestDeclarations = fs.readFileSync(path.join(rootDir, '.svg-sprite', 'svg-sprite.manifest.d.ts'), 'utf8') assert.match(entry, /new URL\('\.\/sprite\.svg', import\.meta\.url\)\.href/) assert.match(entry, /export function getIconsIconHref\(icon\)/) + assert.match(entry, /export const iconsIconTagName = "icons-icon"/) + assert.match(entry, /export function defineIconsIconElement\(\)/) assert.match(manifest, /"mode": "standalone@webpack"/) assert.match(manifest, /new URL\('\.\/sprite\.svg', import\.meta\.url\)\.href/) - assert.doesNotMatch(entry, /react|jsx|\.css/) + assert.match(manifestDeclarations, /export type SpriteManifestData/) + assert.match(manifestDeclarations, /mode: 'standalone@webpack'/) + assert.doesNotMatch(manifestDeclarations, /from '@gromlab\/svg-sprites'/) + assert.doesNotMatch(entry, /react|jsx|\.css|\blit\b/i) + }) -test('switching standalone modes removes obsolete managed files', async () => { +test('switching standalone modes replaces the generated directory', async () => { const rootDir = fixture() const options = { name: 'icons', generatedNotice: false } await generateSprite(rootDir, { ...options, mode: 'standalone@vite' }) + assert.equal(fs.existsSync(path.join(rootDir, '.gitignore')), true) fs.writeFileSync(path.join(rootDir, '.svg-sprite', 'user.txt'), 'keep') await generateSprite(rootDir, { ...options, mode: 'standalone' }) assert.deepEqual(generatedFiles(rootDir), [ 'sprite.svg', - 'state.json', 'svg-sprite.manifest.json', - 'user.txt', ]) - assert.equal(fs.readFileSync(path.join(rootDir, '.svg-sprite', 'user.txt'), 'utf8'), 'keep') + assert.equal(fs.existsSync(path.join(rootDir, '.gitignore')), false) + assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'user.txt')), false) await generateSprite(rootDir, { ...options, mode: 'standalone@webpack' }) + assert.equal(fs.existsSync(path.join(rootDir, '.gitignore')), true) assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'svg-sprite.manifest.json')), false) assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'index.js')), true) - assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'user.txt')), true) + assert.equal(fs.existsSync(path.join(rootDir, '.svg-sprite', 'user.txt')), false) }) From 44afec7cdbab60d0d73fcacde3658dc552b25745 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Wed, 15 Jul 2026 12:27:46 +0300 Subject: [PATCH 08/11] sync --- README_RU.md | 79 ++--- docs/ru/README.md | 7 +- docs/ru/configuration.md | 99 ++++++ docs/ru/guides/AGENTS.md | 78 +++++ docs/ru/guides/next-app-turbopack.md | 145 +++------ docs/ru/guides/next-app-webpack.md | 145 +++------ docs/ru/guides/next-pages-turbopack.md | 138 +++------ docs/ru/guides/next-pages-webpack.md | 138 +++------ docs/ru/guides/react-vite.md | 160 ++++------ docs/ru/guides/react-webpack.md | 165 +++++----- docs/ru/guides/standalone-vite.md | 155 ++++----- docs/ru/guides/standalone-webpack.md | 142 ++++----- docs/ru/next-app.md | 4 - docs/ru/next-pages.md | 4 - docs/ru/programmatic-api.md | 3 - docs/ru/react-vite.md | 3 - docs/ru/react-webpack.md | 3 - docs/ru/reference.md | 3 - docs/ru/reference/programmatic-api.md | 31 +- docs/ru/reference/technical.md | 36 ++- skills/README.md | 24 +- skills/svg-sprites/build.mjs | 8 +- skills/svg-sprites/skill.config.mjs | 33 +- skills/svg-sprites/src/ru/SKILL.md | 293 +++++++++++++++++- .../src/ru/core/00-package-overview.md | 16 - .../src/ru/core/10-mode-selection.md | 30 -- .../src/ru/core/20-project-inspection.md | 22 -- .../src/ru/core/30-react-next-setup.md | 63 ---- .../src/ru/core/40-generated-contract.md | 51 --- .../src/ru/core/50-usage-and-colors.md | 87 ------ .../src/ru/core/60-verification.md | 15 - .../svg-sprites/src/ru/core/70-diagnostics.md | 24 -- .../src/ru/references/complex-svg.md | 2 +- .../src/ru/references/programmatic-api.md | 46 --- test/documentation-guides.test.mjs | 28 +- 35 files changed, 1056 insertions(+), 1224 deletions(-) create mode 100644 docs/ru/configuration.md create mode 100644 docs/ru/guides/AGENTS.md delete mode 100644 docs/ru/next-app.md delete mode 100644 docs/ru/next-pages.md delete mode 100644 docs/ru/programmatic-api.md delete mode 100644 docs/ru/react-vite.md delete mode 100644 docs/ru/react-webpack.md delete mode 100644 docs/ru/reference.md delete mode 100644 skills/svg-sprites/src/ru/core/00-package-overview.md delete mode 100644 skills/svg-sprites/src/ru/core/10-mode-selection.md delete mode 100644 skills/svg-sprites/src/ru/core/20-project-inspection.md delete mode 100644 skills/svg-sprites/src/ru/core/30-react-next-setup.md delete mode 100644 skills/svg-sprites/src/ru/core/40-generated-contract.md delete mode 100644 skills/svg-sprites/src/ru/core/50-usage-and-colors.md delete mode 100644 skills/svg-sprites/src/ru/core/60-verification.md delete mode 100644 skills/svg-sprites/src/ru/core/70-diagnostics.md delete mode 100644 skills/svg-sprites/src/ru/references/programmatic-api.md diff --git a/README_RU.md b/README_RU.md index a41fcdc..0fe049f 100644 --- a/README_RU.md +++ b/README_RU.md @@ -1,6 +1,6 @@ # @gromlab/svg-sprites -[🇬🇧 English](README.md) | 🇷🇺 Русский +[🇬🇧 English](https://github.com/gromlab-ru/svg-sprites/blob/master/README.md) | 🇷🇺 Русский ![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) @@ -24,20 +24,6 @@ В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён. -В `standalone@vite` и `standalone@webpack` тот же подход доступен без React: - -```ts -import { defineAppIconElement } from './app-icons' - -defineAppIconElement() -``` - -```html - -``` - -Bare `standalone` остаётся минимальным и генерирует только SVG asset и JSON manifest без JavaScript runtime. - ## AI-friendly из коробки `@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации. @@ -46,61 +32,54 @@ Bare `standalone` остаётся минимальным и генерируе [🇬🇧 Скачать AI skill (на английском)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites.zip) -## От SVG до компонента за четыре шага +## От SVG до компонента за три шага Основной пример использует Next.js App Router и Turbopack. -### 1. Генерируйте без установки пакета +### 1. Укажите нужные иконки -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --help -``` - -`npx` временно скачивает CLI, не добавляет `@gromlab/svg-sprites` в -`package.json`, а generated production runtime не импортирует package. - -### 2. Укажите нужные иконки - -SVG могут оставаться в существующей структуре проекта: +Создайте папки для исходных иконок и спрайта: ```text -src/ -├── assets/icons/ -│ ├── search.svg -│ └── settings.svg -├── features/profile/ -│ └── user.svg -└── ui/app-icons/ - └── svg-sprite.config.ts +assets/ +├── app-icons/ +│ └── svg-sprite.config.json +└── svg-icons/ + ├── search.svg + └── settings.svg ``` Создайте конфигурацию спрайта: -```ts -// src/ui/app-icons/svg-sprite.config.ts -export default { - mode: 'next@app/turbopack', - name: 'app', - input: [ - '../../assets/icons/search.svg', - '../../assets/icons/settings.svg', - '../../features/profile/user.svg', - ], +```json +{ + "mode": "next@app/turbopack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -### 3. Добавьте генерацию +`input` поддерживает пути к папкам, отдельным SVG и glob-шаблоны. + +### 2. Добавьте генерацию ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", "predev": "npm run sprites", "prebuild": "npm run sprites" } } ``` +Создайте точку входа для сгенерированного API: + +```ts +// assets/app-icons/index.ts +export * from './.svg-sprite/index.js' +``` + Первый запуск: ```bash @@ -109,10 +88,11 @@ npm run sprites Пакет создаст `AppIcon`, TypeScript-типы и отдельный SVG-спрайт. -### 4. Используйте как обычную иконку +### 3. Используйте как обычную иконку ```tsx -import { AppIcon } from '@/ui/app-icons' +// app/page.tsx +import { AppIcon } from '../assets/app-icons' export default function SearchButton() { return ( @@ -277,6 +257,7 @@ README знакомит с возможностями проекта и пока ### Технические материалы - [Индекс документации](docs/ru/README.md) +- [Конфигурация](docs/ru/configuration.md) - [Технический справочник](docs/ru/reference/technical.md) - [Программный API](docs/ru/reference/programmatic-api.md) diff --git a/docs/ru/README.md b/docs/ru/README.md index e226abb..c68ddf5 100644 --- a/docs/ru/README.md +++ b/docs/ru/README.md @@ -3,6 +3,8 @@ Для настройки выберите guide одного exact mode. Каждый guide является самостоятельным документом и без изменений используется в AI skills. +Общий формат JSON, JavaScript и TypeScript config-файлов описан в [руководстве по конфигурации](configuration.md). + ## Гайды быстрого старта | Проект | Exact mode | Guide | @@ -20,10 +22,11 @@ Все guides используют один порядок: 1. Генерация спрайта через `npx` без добавления package в проект. -2. Необязательное подключение Viewer для дебага и превью. -3. Необязательная типизация конфига через package или локальный copy-paste type. +2. Использование спрайта в приложении. +3. Необязательное подключение Viewer для дебага и превью. ## Справочники +- [Конфигурация](configuration.md) - [Технический справочник](reference/technical.md) - [Программный API](reference/programmatic-api.md) diff --git a/docs/ru/configuration.md b/docs/ru/configuration.md new file mode 100644 index 0000000..2a16704 --- /dev/null +++ b/docs/ru/configuration.md @@ -0,0 +1,99 @@ +# Конфигурация + +Каждый config-файл описывает один независимый спрайт. CLI не ищет конфиг автоматически, поэтому всегда передавайте путь явно: + +```bash +npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json +``` + +## JSON + +JSON подходит для большинства проектов и не требует локальной установки пакета: + +```json +{ + "mode": "next@app/turbopack", + "name": "app", + "description": "Общие иконки приложения", + "input": [ + "./icons", + "../../assets/icons/**/*.svg", + "!../../assets/icons/deprecated-*.svg" + ], + "transform": { + "removeSize": true, + "replaceColors": true, + "addTransition": true + }, + "generatedNotice": true +} +``` + +| Поле | По умолчанию | Назначение | +|---|---|---| +| `mode` | Нет | Exact mode, соответствующий framework и сборщику | +| `name` | Kebab-case имени каталога модуля; для `svg-sprite` и `svg-sprites` — имени родительского каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и типов | +| `description` | Нет | Описание для типов и Viewer | +| `input` | `./icons` | Каталог, SVG-файл, glob-шаблон или массив источников | +| `transform` | Все включены | Настройки подготовки SVG | +| `generatedNotice` | `true` | Вид предупреждения в generated-файлах | + +Пути и glob-шаблоны в `input` считаются от каталога config-файла. Паттерн с префиксом `!` исключает совпадения. + +## JavaScript + +JavaScript-конфиг экспортирует обычный объект по умолчанию: + +```js +export default { + mode: 'react@vite', + name: 'icons', + input: './icons', +} +``` + +Передайте CLI путь к `.js`-файлу так же, как к JSON: + +```bash +npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.js +``` + +## TypeScript + +Для проверки конфига TypeScript установите пакет как dev dependency: + +```bash +npm install --save-dev @gromlab/svg-sprites +``` + +Используйте `defineSpriteConfig`: + +```ts +import { defineSpriteConfig } from '@gromlab/svg-sprites' + +export default defineSpriteConfig({ + mode: 'react@vite', + name: 'icons', + input: './icons', +}) +``` + +Или примените `satisfies` с type-only импортом: + +```ts +import type { SpriteConfig } from '@gromlab/svg-sprites' + +export default { + mode: 'react@vite', + name: 'icons', + input: './icons', +} satisfies SpriteConfig +``` + +CLI загружает `.ts`-конфиг напрямую: + +```bash +npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.ts +``` + +Полный список modes, CLI-флагов, правил именования и transform-опций находится в [техническом справочнике](reference/technical.md). diff --git a/docs/ru/guides/AGENTS.md b/docs/ru/guides/AGENTS.md new file mode 100644 index 0000000..472147f --- /dev/null +++ b/docs/ru/guides/AGENTS.md @@ -0,0 +1,78 @@ +# Правила гайдов быстрого старта + +## Цель + +Гайд должен помочь читателю как можно быстрее создать SVG-спрайт и использовать его в своём приложении. + +Спрайт является главным результатом. Компоненты и другие сгенерированные файлы описываются только как средства его использования. + +## Область гайда + +Каждый гайд посвящён одному exact mode. + +В гайд включаются только действия и особенности, относящиеся к этому mode. Нельзя переносить в него поведение других сборщиков, фреймворков или modes. + +Все технические утверждения необходимо проверять по реализации соответствующего adapter. + +## Структура + +Гайд состоит из трёх основных частей: + +1. Генерация спрайта. +2. Использование спрайта. +3. Дебаг и превью через Viewer. + +Первая строка после заголовка должна объяснять, что это инструкция по быстрому созданию SVG-спрайта и для какого приложения она предназначена. + +## Примеры + +Все примеры внутри гайда должны составлять один последовательный сценарий. + +Пути, имена, команды, импорты и названия сгенерированных API должны соответствовать друг другу и фактическому результату генерации. + +Во всех гайдах используются согласованные примеры: + +- исходные SVG находятся в `assets/svg-icons`; +- спрайт создаётся в `assets/app-icons`; +- конфиг записывается в JSON; +- имя спрайта в конфиге — `app`. + +## Зависимости + +В разделе генерации нужно явно показать ключевое преимущество: для создания и использования спрайта пакет не требуется добавлять в зависимости проекта. + +Viewer описывается отдельно как необязательный инструмент разработки. Установка или подключение пакета допускается только в разделе Viewer и только способом, подходящим текущему mode. + +## Содержание + +Гайд должен содержать только минимальный рабочий путь: + +- структуру проекта; +- конфиг; +- команду генерации; +- автоматическую генерацию перед запуском и сборкой, если она необходима; +- подключение иконки; +- базовую настройку размера и цветов; +- подключение Viewer. + +Особенности mode добавляются только тогда, когда без них пример не работает или работает неправильно. + +## Технический шум + +Не нужно описывать внутреннее устройство генератора, сгенерированных файлов и сборщика. + +Не нужно перечислять альтернативные конфигурации, дополнительные API, редкие сценарии и ограничения, не относящиеся к быстрому старту. + +Подробности должны оставаться в reference-документации. + +## Стиль + +Писать для пользователя, а не для разработчика библиотеки. + +Использовать короткие, прямые и практические формулировки. + +Сначала объяснять пользу или цель шага, затем показывать действие. + +Не использовать воду, рекламные формулировки и технические термины, которые не помогают выполнить инструкцию. + +Не начинать документ с перечисления сгенерированных компонентов или особенностей реализации. diff --git a/docs/ru/guides/next-app-turbopack.md b/docs/ru/guides/next-app-turbopack.md index b56f876..12d8d66 100644 --- a/docs/ru/guides/next-app-turbopack.md +++ b/docs/ru/guides/next-app-turbopack.md @@ -1,153 +1,108 @@ -# Next.js App Router с Turbopack +# SVG-спрайт для Next.js App Router с Turbopack -Это автономный quick start для exact mode key `next@app/turbopack`: generated `IconsIcon` совместим с Server Components и asset pipeline Turbopack. +Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Turbopack. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'next@app/turbopack', - name: 'icons', +```json +{ + "mode": "next@app/turbopack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг может быть `.ts`, `.js` с `default export` или `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Mode и команды Next должны указывать один bundler: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && next dev --turbopack", - "build": "npm run sprites && next build --turbopack", - "start": "next start", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "next dev --turbopack", + "prebuild": "npm run sprites", + "build": "next build --turbopack" } } ``` -Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его `.d.ts` self-contained и не импортируют generator package. +## Использование спрайта + +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Generated icon не содержит `'use client'`, поэтому его можно импортировать прямо в Server Component: +Используйте компонент в Server Component: ```tsx // app/page.tsx -import { IconsIcon, iconsIconNames } from '../src/sprite' +import { AppIcon } from '../assets/app-icons' export default function Page() { return ( -
- - {iconsIconNames.length} иконок -
+ ) } ``` -Turbopack обрабатывает generated `new URL('../sprite.svg', import.meta.url).href` и выпускает внешний hashed asset. Не добавляйте Client Component boundary только ради `IconsIcon`. +Для `AppIcon` не нужен `'use client'`. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно. -## 2. Дебаг и превью +## Дебаг и превью -Viewer необязателен и нужен только для debug/preview: +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Viewer интерактивен, поэтому создайте для него отдельный Client Component: +Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`: ```tsx -// app/icons-debug/sprite-viewer.tsx 'use client' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export function AppSpriteViewer() { +export function SvgSpriteViewer() { return } ``` -Server page импортирует только эту boundary: +Создайте маршрут `app/svg-sprite/page.tsx`: ```tsx -// app/icons-debug/page.tsx -import { AppSpriteViewer } from './sprite-viewer' +import { notFound } from 'next/navigation' -export default function IconsDebugPage() { - return +import { SvgSpriteViewer } from './SvgSpriteViewer' + +export default function SvgSpritePage() { + if (process.env.NODE_ENV !== 'development') notFound() + + return } ``` -Viewer не входит в production icon runtime и не нужен обычным страницам с `IconsIcon`. - -## 3. Типизация конфига - -После локальной установки package используйте helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/turbopack', - name: 'icons', -}) -``` - -Возможен и type-only import `SpriteConfig` с `satisfies SpriteConfig`. - -Без package добавьте локальный type в config: - -```ts -type LocalSpriteConfig = { - mode: 'next@app/turbopack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'next@app/turbopack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Exact literal защищает от смешивания App Router и других bundler contracts. +Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404. diff --git a/docs/ru/guides/next-app-webpack.md b/docs/ru/guides/next-app-webpack.md index 40d10b0..493f202 100644 --- a/docs/ru/guides/next-app-webpack.md +++ b/docs/ru/guides/next-app-webpack.md @@ -1,151 +1,108 @@ -# Next.js App Router с Webpack +# SVG-спрайт для Next.js App Router с Webpack -Это автономный quick start для exact mode key `next@app/webpack`: generated `IconsIcon` совместим с Server Components и Webpack pipeline Next.js. +Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Webpack. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'next@app/webpack', - name: 'icons', +```json +{ + "mode": "next@app/webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Также поддерживаются `.js` с `default export` и `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI замените `latest` точной версией, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для этого mode: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && next dev --webpack", - "build": "npm run sprites && next build --webpack", - "start": "next start", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "next dev --webpack", + "prebuild": "npm run sprites", + "build": "next build --webpack" } } ``` -Не сочетайте явный `npm run sprites` с `predev` или `prebuild`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не зависят от `@gromlab/svg-sprites`. +## Использование спрайта + +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage в Server Component: +Используйте компонент в Server Component: ```tsx // app/page.tsx -import { IconsIcon, iconsIconNames } from '../src/sprite' +import { AppIcon } from '../assets/app-icons' export default function Page() { return ( -
- - {iconsIconNames.length} иконок -
+ ) } ``` -Generated component не содержит `'use client'`. Next Webpack обрабатывает `new URL('../sprite.svg', import.meta.url).href` и публикует отдельный SVG asset; не переписывайте этот URL и не переносите sprite в `public` вручную. +Для `AppIcon` не нужен `'use client'`. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно. -## 2. Дебаг и превью +## Дебаг и превью -Viewer необязателен. Для debug/preview установите package: +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -App Router требует отдельную Client Component boundary для Viewer: +Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`: ```tsx -// app/icons-debug/sprite-viewer.tsx 'use client' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export function AppSpriteViewer() { +export function SvgSpriteViewer() { return } ``` +Создайте маршрут `app/svg-sprite/page.tsx`: + ```tsx -// app/icons-debug/page.tsx -import { AppSpriteViewer } from './sprite-viewer' +import { notFound } from 'next/navigation' -export default function IconsDebugPage() { - return +import { SvgSpriteViewer } from './SvgSpriteViewer' + +export default function SvgSpritePage() { + if (process.env.NODE_ENV !== 'development') notFound() + + return } ``` -Статический loader позволяет Webpack связать manifest и emitted SVG. Viewer не входит в production runtime `IconsIcon`. - -## 3. Типизация конфига - -При локально установленном package используйте helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@app/webpack', - name: 'icons', -}) -``` - -Другой package-вариант: type-only import `SpriteConfig` и `satisfies SpriteConfig`. - -Без package вставьте локальный type прямо в config: - -```ts -type LocalSpriteConfig = { - mode: 'next@app/webpack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'next@app/webpack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Локальный exact literal исключает случайную генерацию Turbopack или Pages Router output. +Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404. diff --git a/docs/ru/guides/next-pages-turbopack.md b/docs/ru/guides/next-pages-turbopack.md index b3e104a..638c690 100644 --- a/docs/ru/guides/next-pages-turbopack.md +++ b/docs/ru/guides/next-pages-turbopack.md @@ -1,140 +1,98 @@ -# Next.js Pages Router с Turbopack +# SVG-спрайт для Next.js Pages Router с Turbopack -Это автономный quick start для exact mode key `next@pages/turbopack`: generated `IconsIcon` работает при SSR, SSG и клиентских переходах Pages Router. +Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Turbopack. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'next@pages/turbopack', - name: 'icons', +```json +{ + "mode": "next@pages/turbopack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -Для CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact commands должны сохранять Turbopack и для dev, и для production build: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && next dev --turbopack", - "build": "npm run sprites && next build --turbopack", - "start": "next start", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "next dev --turbopack", + "prebuild": "npm run sprites", + "build": "next build --turbopack" } } ``` -Не добавляйте `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package. +## Использование спрайта + +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage на обычной page: +Используйте компонент на странице: ```tsx // pages/index.tsx -import { IconsIcon, iconsIconNames } from '../src/sprite' +import { AppIcon } from '../assets/app-icons' export default function Page() { return ( -
- - {iconsIconNames.length} иконок -
+ ) } ``` -Компонент одинаково работает с `getServerSideProps`, `getStaticProps` и client navigation. Turbopack разрешает generated `new URL('../sprite.svg', import.meta.url).href` в отдельный hashed asset. +Компонент работает с SSR, SSG и клиентскими переходами. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно. -## 2. Дебаг и превью +## Дебаг и превью -Viewer необязателен и нужен только для debug/preview: +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -В Pages Router Viewer можно использовать прямо в page, без отдельной App Router Client Component boundary: +Создайте страницу `pages/svg-sprite.tsx`: ```tsx -// pages/icons-debug.tsx +import type { GetStaticProps } from 'next' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export default function IconsDebugPage() { +export default function SvgSpritePage() { return } + +export const getStaticProps: GetStaticProps = () => + process.env.NODE_ENV === 'development' + ? { props: {} } + : { notFound: true } ``` -Оставляйте эту page только во внутреннем debug-разделе. Viewer не входит в production icon runtime `IconsIcon`. - -## 3. Типизация конфига - -Если package установлен локально, используйте helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/turbopack', - name: 'icons', -}) -``` - -Альтернатива: type-only import `SpriteConfig` и объект `satisfies SpriteConfig`. - -Без package добавьте copy-paste type в config: - -```ts -type LocalSpriteConfig = { - mode: 'next@pages/turbopack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'next@pages/turbopack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Exact literal не позволяет незаметно смешать Pages Router с App Router или Webpack output. +Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404. diff --git a/docs/ru/guides/next-pages-webpack.md b/docs/ru/guides/next-pages-webpack.md index 072b7a7..d98d63a 100644 --- a/docs/ru/guides/next-pages-webpack.md +++ b/docs/ru/guides/next-pages-webpack.md @@ -1,140 +1,98 @@ -# Next.js Pages Router с Webpack +# SVG-спрайт для Next.js Pages Router с Webpack -Это автономный quick start для exact mode key `next@pages/webpack`: generated `IconsIcon` работает в Pages Router и публикует SVG через Webpack pipeline Next.js. +Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Webpack. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'next@pages/webpack', - name: 'icons', +```json +{ + "mode": "next@pages/webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI замените `latest` на точную проверенную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для Webpack: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && next dev --webpack", - "build": "npm run sprites && next build --webpack", - "start": "next start", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "next dev --webpack", + "prebuild": "npm run sprites", + "build": "next build --webpack" } } ``` -Не дублируйте эти вызовы через `predev`, `prebuild` или `pretypecheck`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не требуют `@gromlab/svg-sprites`. +## Использование спрайта + +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage: +Используйте компонент на странице: ```tsx // pages/index.tsx -import { IconsIcon, iconsIconNames } from '../src/sprite' +import { AppIcon } from '../assets/app-icons' export default function Page() { return ( -
- - {iconsIconNames.length} иконок -
+ ) } ``` -Компонент поддерживает SSR, SSG и клиентские переходы. Next Webpack преобразует generated `new URL('../sprite.svg', import.meta.url).href` во внешний hashed asset; не конструируйте URL спрайта вручную. +Компонент работает с SSR, SSG и клиентскими переходами. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно. -## 2. Дебаг и превью +## Дебаг и превью -Viewer необязателен. Устанавливайте package только для debug/preview: +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Pages Router позволяет разместить Viewer непосредственно в page без отдельной App Router boundary: +Создайте страницу `pages/svg-sprite.tsx`: ```tsx -// pages/icons-debug.tsx +import type { GetStaticProps } from 'next' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export default function IconsDebugPage() { +export default function SvgSpritePage() { return } + +export const getStaticProps: GetStaticProps = () => + process.env.NODE_ENV === 'development' + ? { props: {} } + : { notFound: true } ``` -Статический loader даёт Webpack точный manifest module и связанный SVG asset. Не импортируйте Viewer из production pages, если preview там не нужен; runtime `IconsIcon` от него независим. - -## 3. Типизация конфига - -После локальной установки package доступен helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'next@pages/webpack', - name: 'icons', -}) -``` - -Также можно применить `satisfies SpriteConfig` с type-only импортом `SpriteConfig`. - -Без package вставьте локальный type прямо в config: - -```ts -type LocalSpriteConfig = { - mode: 'next@pages/webpack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'next@pages/webpack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Такой exact literal выявляет ошибочный выбор App Router или Turbopack ещё при проверке config. +Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404. diff --git a/docs/ru/guides/react-vite.md b/docs/ru/guides/react-vite.md index cedad15..320168c 100644 --- a/docs/ru/guides/react-vite.md +++ b/docs/ru/guides/react-vite.md @@ -1,141 +1,115 @@ -# React-компонент для Vite +# SVG-спрайт для React на Vite -Это автономный quick start для exact mode key `react@vite`: генератор создаёт типизированный `IconsIcon`, а Vite публикует отдельный SVG asset. +Инструкция по быстрому созданию SVG-спрайта в React-приложении на Vite. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'react@vite', - name: 'icons', +```json +{ + "mode": "react@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Вместо `.ts` можно использовать `.js` с `default export` или `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Vite commands: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && vite", - "build": "npm run sprites && tsc --noEmit && vite build", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "tsc --noEmit && vite build" } } ``` -Не добавляйте `predev`, `prebuild` или `pretypecheck`, если соответствующие scripts уже явно запускают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained: они описывают компонент и manifest без импорта `@gromlab/svg-sprites`. +## Использование спрайта -Пользовательский barrel возвращает generated API: +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage: +Используйте компонент в приложении: ```tsx -import { IconsIcon, iconsIconNames } from './sprite' +import { AppIcon } from '../assets/app-icons' -export function SaveButton() { +export function SaveIcon() { return ( - + ) } - -console.log(iconsIconNames) ``` -Prop `icon` является union имён исходных файлов. Vite автоматически обрабатывает generated CSS Module и импорт `sprite.svg?no-inline`; query запрещает inline и заставляет Vite выпустить отдельный hashed SVG asset. Если TypeScript не знает Vite asset imports, добавьте `/// ` в `src/vite-env.d.ts`. +Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`. -## 2. Дебаг и превью +Vite сам подключает стили компонента и добавляет `sprite.svg` в итоговую сборку. -Viewer необязателен и нужен только для debug/preview. Установите package отдельно: +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Используйте React bridge со статическим массивом loaders: +Создайте `svg-sprite.html` в корне проекта: + +```html + + + + + Иконки проекта + + + +
+ + + + + +``` + +Создайте `src/svg-sprite-debug.tsx`: ```tsx +import { createRoot } from 'react-dom/client' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export function IconsDebugPage() { - return -} +createRoot(document.getElementById('svg-sprite-viewer')!).render( + , +) ``` -Строковый путь в `import()` должен указывать на generated JS manifest. Держите страницу за debug-маршрутом; `SpriteViewer` не входит в production runtime `IconsIcon`. +Запустите `npm run dev` и откройте `/svg-sprite.html`. -## 3. Типизация конфига - -Если package установлен локально, используйте helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@vite', - name: 'icons', -}) -``` - -Также можно импортировать `SpriteConfig` только как type и написать объект `satisfies SpriteConfig`. - -Без package добавьте локальный type прямо в config: - -```ts -type LocalSpriteConfig = { - mode: 'react@vite' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'react@vite', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Локальный type проверяет только этот exact mode и не создаёт runtime-зависимость. +Стандартная production-сборка Vite использует только `index.html` и не включает страницу Viewer. diff --git a/docs/ru/guides/react-webpack.md b/docs/ru/guides/react-webpack.md index a7591a2..b265fc7 100644 --- a/docs/ru/guides/react-webpack.md +++ b/docs/ru/guides/react-webpack.md @@ -1,141 +1,126 @@ -# React-компонент для Webpack 5 +# SVG-спрайт для React на Webpack 5 -Это автономный quick start для exact mode key `react@webpack`: generated `IconsIcon` использует Webpack 5 Asset Modules и CSS Modules. +Инструкция по быстрому созданию SVG-спрайта в React-приложении на Webpack 5. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный plain config: - -```ts -export default { - mode: 'react@webpack', - name: 'icons', +```json +{ + "mode": "react@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && webpack serve --mode development", - "build": "npm run sprites && webpack --mode production", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" } } ``` -Не сочетайте эти явные вызовы с `predev`/`prebuild`: иначе генерация задублируется. `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package. +## Использование спрайта + +Значение `name: "app"` создаёт React-компонент `AppIcon`. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage: +Используйте компонент в приложении: ```tsx -import { IconsIcon, iconsIconNames } from './sprite' +import { AppIcon } from '../assets/app-icons' -export function SaveButton() { +export function SaveIcon() { return ( - + ) } - -console.log(iconsIconNames) ``` -Generated component получает URL через `new URL('../sprite.svg', import.meta.url).href`. Webpack 5 должен обработать SVG как Asset Module. Исключите generated `sprite.svg` из `@svgr/webpack`, inline/raw loaders и других общих SVG rules либо задайте для него `type: 'asset/resource'`. +Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`. -Компонент импортирует `react-component.module.css`. Webpack config должен обрабатывать `*.module.css` через `css-loader` с CSS Modules и `style-loader` или `MiniCssExtractPlugin`. Для TypeScript при необходимости добавьте декларацию `declare module '*.module.css'`. +Компонент использует CSS Modules. Если проект ещё не обрабатывает их с default export, добавьте правило в `webpack.config.js`: -## 2. Дебаг и превью +```js +{ + test: /\.module\.css$/i, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { modules: { namedExport: false } }, + }, + ], +} +``` -Viewer необязателен. Устанавливайте package только для debug/preview: +Webpack 5 сам добавляет `sprite.svg` в итоговую сборку. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки. + +Установите Viewer: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Webpack не использует `import.meta.glob`; передайте статический loader: +Создайте entry `src/svg-sprite-debug.tsx`: ```tsx +import { createRoot } from 'react-dom/client' import { SpriteViewer } from '@gromlab/svg-sprites/react' const sources = [ - () => import('./sprite/.svg-sprite/svg-sprite.manifest.js'), + () => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'), ] as const -export function IconsDebugPage() { - return -} +const container = document.createElement('div') +document.body.append(container) + +createRoot(container).render( + , +) ``` -Webpack создаст chunk manifest и разрешит его SVG через тот же Asset Modules pipeline. Viewer держите только в debug route; production `IconsIcon` от него не зависит. +Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`: -## 3. Типизация конфига - -С локально установленным package доступен helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'react@webpack', - name: 'icons', +```js +export default (_env, argv) => ({ + // Остальные настройки Webpack. + entry: [ + './src/main.tsx', + ...(argv.mode === 'development' ? ['./src/svg-sprite-debug.tsx'] : []), + ], }) ``` -Эквивалентная проверка: type-only import `SpriteConfig` и `satisfies SpriteConfig`. - -Без package используйте copy-paste type в config: - -```ts -type LocalSpriteConfig = { - mode: 'react@webpack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'react@webpack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Локальный literal не разрешит случайно выбрать Vite или Next mode. +Запустите `npm run dev`. Viewer появится на основной странице приложения и не попадёт в production-сборку. diff --git a/docs/ru/guides/standalone-vite.md b/docs/ru/guides/standalone-vite.md index 03e53fe..379db66 100644 --- a/docs/ru/guides/standalone-vite.md +++ b/docs/ru/guides/standalone-vite.md @@ -1,151 +1,114 @@ -# Нативный icon Web Component с Vite +# SVG-спрайт для Vite без фреймворка -Это автономный quick start для exact mode key `standalone@vite`: generated facade регистрирует `` и отдаёт SVG в asset pipeline Vite. +Инструкция по быстрому созданию SVG-спрайта в приложении на Vite без фреймворка. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -Рекомендуемая структура держит конфиг и иконки рядом: +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` - -Минимальный plain config: - -```ts -export default { - mode: 'standalone@vite', - name: 'icons', +```json +{ + "mode": "standalone@vite", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact dev/build commands для Vite: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && vite", - "build": "npm run sprites && vite build", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "vite", + "prebuild": "npm run sprites", + "build": "vite build" } } ``` -Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites` в этих scripts. `.svg-sprite` является generated-каталогом и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют `@gromlab/svg-sprites`. +## Использование спрайта -Верните facade через пользовательский barrel: +Значение `name: "app"` создаёт элемент ``. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production entry регистрирует native Web Component: +Зарегистрируйте элемент в `src/main.ts`: ```ts -import { defineIconsIconElement, iconsIconNames } from './sprite' +import { defineAppIconElement } from '../assets/app-icons' +import './style.css' -defineIconsIconElement() - -document.querySelector('#app')!.innerHTML = ` - -` - -console.log(iconsIconNames) +defineAppIconElement() ``` -Generated facade импортирует `sprite.svg?no-inline`: Vite автоматически выпускает отдельный hashed SVG asset и не превращает его в data URL. TypeScript-проекту при необходимости добавьте стандартные Vite types: +Используйте иконку в HTML: -```ts -/// +```html + ``` -Размер по умолчанию равен `1em`, поэтому компонент удобно масштабировать через `font-size`. Цвет задаётся через `color` и generated custom properties: +Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS: ```css -icons-icon { +app-icon { font-size: 24px; color: #334155; --icon-color-2: #f59e0b; } ``` -## 2. Дебаг и превью +Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer. -Viewer необязателен и нужен только для debug/preview. Установите его отдельно: +Vite сам добавит `sprite.svg` в итоговую сборку. Копировать его в `public` не нужно. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Зарегистрируйте Viewer и передайте generated JS manifest через свойство `sources`: +Создайте `svg-sprite.html` в корне проекта: + +```html + + + + + Иконки проекта + + + + + + + + + +``` + +Создайте `src/svg-sprite-debug.ts`: ```ts import '@gromlab/svg-sprites/viewer/element' import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' - -document.querySelector('#app')!.insertAdjacentHTML( - 'beforeend', - '', -) +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' const viewer = document.querySelector('gromlab-sprite-viewer')! -viewer.viewerTitle = 'Иконки проекта' viewer.sources = [spriteManifest] ``` -Расположите этот код только в debug entry или внутреннем маршруте. Viewer не входит в production runtime ``. +Запустите `npm run dev` и откройте `/svg-sprite.html`. -## 3. Типизация конфига - -При локально установленном package используйте helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'standalone@vite', - name: 'icons', -}) -``` - -Альтернатива с package: `import type { SpriteConfig }` и объект `satisfies SpriteConfig`. - -Без package скопируйте локальный type прямо в config: - -```ts -type LocalSpriteConfig = { - mode: 'standalone@vite' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'standalone@vite', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Локальный type ограничивает `mode` одним exact literal и ничего не загружает во время генерации. +Viewer не требуется для работы `` и не подключается к основному коду приложения. diff --git a/docs/ru/guides/standalone-webpack.md b/docs/ru/guides/standalone-webpack.md index f60ca05..bc3bb31 100644 --- a/docs/ru/guides/standalone-webpack.md +++ b/docs/ru/guides/standalone-webpack.md @@ -1,145 +1,111 @@ -# Нативный icon Web Component с Webpack 5 +# SVG-спрайт для Webpack 5 без фреймворка -Это автономный quick start для exact mode key `standalone@webpack`: generated facade предоставляет ``, а Webpack 5 публикует SVG через Asset Modules. +Инструкция по быстрому созданию SVG-спрайта в приложении на Webpack 5 без фреймворка. -## 1. Генерация спрайта +## Генерация спрайта -Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`. +Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`. -```text -src/sprite/ -├── icons/ -│ ├── check.svg -│ └── warning.svg -├── index.ts -└── svg-sprite.config.ts -``` +Создайте конфиг `assets/app-icons/svg-sprite.config.json`: -Минимальный config рядом с `icons/`: - -```ts -export default { - mode: 'standalone@webpack', - name: 'icons', +```json +{ + "mode": "standalone@webpack", + "name": "app", + "input": "../svg-icons/**/*.svg" } ``` -Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются также `.js` с `default export` и `.json`. +Путь в `input` считается от папки с конфигом. -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts -``` - -Для CI закрепите точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands: +Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой: ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts", - "dev": "npm run sprites && webpack serve --mode development", - "build": "npm run sprites && webpack --mode production", - "typecheck": "npm run sprites && tsc --noEmit" + "sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json", + "predev": "npm run sprites", + "dev": "webpack serve --mode development", + "prebuild": "npm run sprites", + "build": "webpack --mode production" } } ``` -Не дублируйте запуск через `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его declarations self-contained и не требуют `@gromlab/svg-sprites`. +## Использование спрайта + +Значение `name: "app"` создаёт элемент ``. + +Создайте точку входа `assets/app-icons/index.ts`: ```ts -// src/sprite/index.ts export * from './.svg-sprite/index.js' ``` -Production usage: +Зарегистрируйте элемент в основном entry приложения: ```ts -import { defineIconsIconElement, iconsIconNames } from './sprite' +import { defineAppIconElement } from '../assets/app-icons' +import './style.css' -defineIconsIconElement() - -document.querySelector('#app')!.innerHTML = ` - -` - -console.log(iconsIconNames) +defineAppIconElement() ``` -Generated facade использует `new URL('./sprite.svg', import.meta.url).href`. Webpack 5 Asset Modules выпускают отдельный asset; его итоговый URL учитывает `output.publicPath` и `assetModuleFilename`. +Используйте иконку в HTML: -Если проект использует `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общий SVG rule, исключите `src/sprite/.svg-sprite/sprite.svg` из этого правила. Generated SVG должен обрабатываться как `asset/resource`, а не как React-компонент или inline source. +```html + +``` -Размер Web Component по умолчанию `1em`; управляйте им и цветами обычным CSS: +Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS: ```css -icons-icon { +app-icon { font-size: 24px; color: #334155; --icon-color-2: #f59e0b; } ``` -## 2. Дебаг и превью +Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer. -Viewer необязателен. Для debug/preview установите package как dev dependency: +Webpack 5 сам добавит `sprite.svg` в итоговую сборку. + +## Дебаг и превью + +Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки. + +Установите Viewer: ```bash npm install --save-dev @gromlab/svg-sprites ``` -Подключите element entry и generated JS manifest: +Создайте entry `src/svg-sprite-debug.ts`: ```ts import '@gromlab/svg-sprites/viewer/element' import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' -import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js' +import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js' -document.querySelector('#app')!.insertAdjacentHTML( - 'beforeend', - '', -) - -const viewer = document.querySelector('gromlab-sprite-viewer')! +const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement viewer.viewerTitle = 'Иконки проекта' viewer.sources = [spriteManifest] +document.body.append(viewer) ``` -Webpack свяжет manifest с тем же emitted SVG asset. Оставляйте Viewer только в debug entry: production `` от него не зависит. +Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`: -## 3. Типизация конфига - -После локальной установки package можно использовать helper: - -```ts -import { defineSpriteConfig } from '@gromlab/svg-sprites' - -export default defineSpriteConfig({ - mode: 'standalone@webpack', - name: 'icons', +```js +export default (_env, argv) => ({ + // Остальные настройки Webpack. + entry: [ + './src/main.ts', + ...(argv.mode === 'development' ? ['./src/svg-sprite-debug.ts'] : []), + ], }) ``` -Либо импортируйте только `SpriteConfig` как type и примените `satisfies SpriteConfig`. +Запустите `npm run dev`. Viewer появится на основной странице приложения. -Без package добавьте copy-paste type в сам config: - -```ts -type LocalSpriteConfig = { - mode: 'standalone@webpack' - name?: string - description?: string - input?: string | string[] - transform?: { - removeSize?: boolean - replaceColors?: boolean - addTransition?: boolean - } - generatedNotice?: boolean -} - -export default { - mode: 'standalone@webpack', - name: 'icons', -} satisfies LocalSpriteConfig -``` - -Этот вариант сохраняет проверку exact mode без runtime import и без записи generator package в проект. +Viewer добавляется только в development-сборку и не попадает в production. diff --git a/docs/ru/next-app.md b/docs/ru/next-app.md deleted file mode 100644 index 9b8acea..0000000 --- a/docs/ru/next-app.md +++ /dev/null @@ -1,4 +0,0 @@ -# Guides Next.js App Router перемещены - -- [App Router + Turbopack](guides/next-app-turbopack.md) -- [App Router + Webpack](guides/next-app-webpack.md) diff --git a/docs/ru/next-pages.md b/docs/ru/next-pages.md deleted file mode 100644 index 07429da..0000000 --- a/docs/ru/next-pages.md +++ /dev/null @@ -1,4 +0,0 @@ -# Guides Next.js Pages Router перемещены - -- [Pages Router + Turbopack](guides/next-pages-turbopack.md) -- [Pages Router + Webpack](guides/next-pages-webpack.md) diff --git a/docs/ru/programmatic-api.md b/docs/ru/programmatic-api.md deleted file mode 100644 index b5e68be..0000000 --- a/docs/ru/programmatic-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# Программный API перемещён - -Canonical документ: [программный API](reference/programmatic-api.md). diff --git a/docs/ru/react-vite.md b/docs/ru/react-vite.md deleted file mode 100644 index 0a92e9b..0000000 --- a/docs/ru/react-vite.md +++ /dev/null @@ -1,3 +0,0 @@ -# Guide React + Vite перемещён - -Canonical guide: [React + Vite](guides/react-vite.md). diff --git a/docs/ru/react-webpack.md b/docs/ru/react-webpack.md deleted file mode 100644 index b47ded9..0000000 --- a/docs/ru/react-webpack.md +++ /dev/null @@ -1,3 +0,0 @@ -# Guide React + Webpack перемещён - -Canonical guide: [React + Webpack](guides/react-webpack.md). diff --git a/docs/ru/reference.md b/docs/ru/reference.md deleted file mode 100644 index caa74ab..0000000 --- a/docs/ru/reference.md +++ /dev/null @@ -1,3 +0,0 @@ -# Технический справочник перемещён - -Canonical документ: [технический справочник](reference/technical.md). diff --git a/docs/ru/reference/programmatic-api.md b/docs/ru/reference/programmatic-api.md index 209c76f..3a644b2 100644 --- a/docs/ru/reference/programmatic-api.md +++ b/docs/ru/reference/programmatic-api.md @@ -14,6 +14,21 @@ const result = await generateSprite( ) ``` +Результат содержит имя и mode спрайта, количество иконок и абсолютные filesystem paths: + +```ts +result.name +result.mode +result.target +result.iconCount +result.rootDir +result.generatedDir +result.spritePath +result.manifestPath +``` + +Next.js modes дополнительно возвращают `router` и `bundler`. + Для static standalone mode `result.spritePath` можно использовать в build-скрипте, чтобы опубликовать SVG по URL приложения: @@ -29,7 +44,7 @@ await copyFile(result.spritePath, 'dist/app-icons/sprite.svg') `spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON manifest доступен через `result.manifestPath` и копируется независимо от SVG. -Первый аргумент принимает полный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог. +Первый аргумент принимает абсолютный или относительный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог. Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом: @@ -107,13 +122,17 @@ await generateNextSprite('path/to/config.ts', { ```ts import { + isSpriteMode, loadSpriteConfig, resolveSpriteConfig, + resolveSpriteConfigSource, validateSpriteConfig, } from '@gromlab/svg-sprites' ``` +- `isSpriteMode(value)` проверяет, является ли значение поддерживаемым exact mode. - `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл. +- `resolveSpriteConfigSource(source)` разрешает путь как config-файл или config-less каталог. - `validateSpriteConfig(value)` выполняет runtime-валидацию объекта. - `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`. @@ -138,6 +157,16 @@ import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer' Browser entry регистрирует ``. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler. +Для ручной регистрации импортируйте runtime без auto-register entry: + +```ts +import { defineSpriteViewerElement } from '@gromlab/svg-sprites/viewer' + +defineSpriteViewerElement() +``` + +Этот entry также экспортирует типы `SpriteViewerElement`, `SpriteViewerManifest`, `SpriteViewerSource`, `SpriteViewerSources` и связанные типы manifest и loaders. + React bridge сохраняет компонентный API: ```tsx diff --git a/docs/ru/reference/technical.md b/docs/ru/reference/technical.md index 0122739..2ccbc54 100644 --- a/docs/ru/reference/technical.md +++ b/docs/ru/reference/technical.md @@ -2,6 +2,8 @@ [Индекс документации](../README.md) +[Конфигурация JSON, JavaScript и TypeScript](../configuration.md) + Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека: - [Bare standalone](../guides/standalone.md) @@ -24,7 +26,7 @@ Для генерации не нужна dependency проекта. Запускайте CLI через `npx`: ```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites path/to/svg-sprite.config.ts +npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json ``` Устанавливайте пакет как development dependency, только если проекту нужны @@ -36,7 +38,7 @@ npm install --save-dev @gromlab/svg-sprites ## CLI и режимы генерации -CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации: +Для генерации CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации: ```text svg-sprites [options] @@ -54,11 +56,11 @@ svg-sprites [options] | Next.js Pages Router + Turbopack | `next@pages/turbopack` | | Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.ts`. +Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.json`. Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`. -Доступны `--mode`, `--name`, `--description`, повторяемый `--input `, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input` полностью заменяет значение `input` из config. +`--help` и `-h` выводят справку без обязательного пути. Для генерации доступны `--mode`, `--name`, `--description`, повторяемый `--input `, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input` полностью заменяет значение `input` из config. В CLI заключайте glob-паттерны в одинарные кавычки, чтобы shell не раскрыл их до запуска генератора: @@ -96,7 +98,7 @@ export default defineSpriteConfig({ | Опция | Тип | По умолчанию | Назначение | |---|---|---|---| | `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API | -| `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов | +| `name` | `string` | Выводится из каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и публичных типов | | `description` | `string` | Нет | Описание для типов и debug manifest | | `input` | `string \| string[]` | `./icons` | Папки, SVG-файлы и glob-паттерны относительно папки конфига | | `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG | @@ -111,7 +113,7 @@ app → AppIcon file-manager → FileManagerIcon ``` -Если `name` не задано, генератор выводит его из каталога. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога. +Если `name` не задано, генератор преобразует имя каталога в kebab-case. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога. ### Источники иконок @@ -141,7 +143,7 @@ file-manager → FileManagerIcon ```text app-icons/ ├── .gitignore -├── svg-sprite.config.ts +├── svg-sprite.config.json ├── index.ts # необязательный пользовательский barrel └── .svg-sprite/ ├── index.js @@ -183,10 +185,10 @@ runtime asset и deployment-neutral manifest data: generated Web Component без внешних runtime-зависимостей. Bare `standalone` намеренно не создаёт JavaScript-компонент. -Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой `index.ts` генератору не принадлежит; при необходимости создайте пользовательский barrel: +`.svg-sprite` полностью управляется генератором и при каждой генерации заменяется целиком. Любые добавленные в него файлы будут удалены. Пользовательские файлы размещайте рядом, например в корневом `index.ts`: ```ts -export * from './.svg-sprite' +export * from './.svg-sprite/index.js' ``` ## Standalone Web Component и TypeScript @@ -307,7 +309,7 @@ folder open.svg → icon="folder open" → id="icon-" ## Множественные спрайты -Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset: +Каждый каталог с конфигом создаёт независимый mode-specific контракт. React и Next.js создают React-компонент и типы, `standalone@vite` и `standalone@webpack` — Web Component и типы, а bare `standalone` — SVG и JSON manifest: ```text app-icons → AppIcon → общие иконки @@ -353,7 +355,7 @@ Static HTML после публикации `.svg-sprite/sprite.svg` прило ``` -Standalone Vite/Webpack предоставляет generated `getIconsIconHref()` и mapping +Standalone Vite/Webpack предоставляет generated `getAppIconHref()` и mapping внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную. Vite: @@ -602,7 +604,7 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем ```json { "scripts": { - "sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts", + "sprites": "npx --yes @gromlab/svg-sprites src/ui/app-icons/svg-sprite.config.ts", "predev": "npm run sprites", "prebuild": "npm run sprites", "pretypecheck": "npm run sprites" @@ -610,22 +612,22 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем } ``` -CI должен выполнять generation script до сборки или проверки типов. Для воспроизводимости замените `latest` на точную версию. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API. +CI должен выполнять generation script до сборки или проверки типов. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API. -Bare `standalone` не создаёт и не изменяет `.gitignore`: приложение само решает, коммитить или игнорировать его `.svg-sprite/`. В остальных modes генератор не перезапишет пользовательский `.gitignore`. Он также откажется перезаписывать пользовательский файл внутри `.svg-sprite`. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API. +Bare `standalone` не создаёт `.gitignore` и сохраняет пользовательский файл. Если после другого mode остался управляемый `.gitignore`, bare mode удалит его. В остальных modes генератор откажется перезаписать пользовательский `.gitignore` без generated marker. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API. ## Диагностика -- Нет `.svg-sprite/index.js`: запустите generation script до импорта generated-модуля. +- Для всех modes, кроме bare `standalone`: если нет `.svg-sprite/index.js`, запустите generation script до импорта generated-модуля. - Не найден источник: передайте существующий config-файл или каталог sprite-модуля. - Не указан mode: добавьте `mode` в config либо передайте `--mode`. - Иконка отсутствует в типе: проверьте `input`, расширение `.svg`, glob-исключения и необходимость `**/*.svg` для вложенных папок. - Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл. -- `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker. +- `Refusing to overwrite a user file`: в корне sprite-модуля находится пользовательский `.gitignore`, который генератор не может заменить. - Иконка не меняет цвет: используйте `` или generated-компонент и проверьте `replaceColors`. - Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders. - Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML. -- Viewer не видит спрайт: проверьте путь к `.svg-sprite/svg-sprite.manifest.js` и выполните генерацию до запуска приложения. +- Viewer не видит спрайт: для bundler modes проверьте путь к `.svg-sprite/svg-sprite.manifest.js`; для bare `standalone` — URL опубликованных `svg-sprite.manifest.json` и `sprite.svg`. Выполните генерацию до запуска приложения. - Build и mode не совпадают: используйте target, соответствующий фактическому сборщику. Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md). diff --git a/skills/README.md b/skills/README.md index eaefff2..f963c3c 100644 --- a/skills/README.md +++ b/skills/README.md @@ -1,31 +1,25 @@ # AI skills -Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Канонические exact-mode guides находятся в `docs/{en,ru}/guides/` и копируются в соответствующий skill без изменения. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. +Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. -Обе языковые версии имеют одинаковую структуру: +Русский skill хранит весь обязательный контекст в одном файле: ```text -src// +src/ru/ ├── SKILL.md -├── core/ -│ ├── 00-package-overview.md -│ ├── 10-mode-selection.md -│ ├── 20-project-inspection.md -│ ├── 30-react-next-setup.md -│ ├── 40-generated-contract.md -│ ├── 50-usage-and-colors.md -│ ├── 60-verification.md -│ └── 70-diagnostics.md └── references/ - ├── programmatic-api.md └── complex-svg.md ``` -`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. Локальный `references/` содержит только agent-specific материалы. Девять файлов из `docs//guides/` копируются в `references/guides/`; английский artifact получает только английские guides, русский только русские. Второй набор mode guides и каталог `references/upstream/` не создаются. +`src/ru/SKILL.md` содержит знания о пакете и рабочий процесс агента. Exact-mode настройка берётся из canonical guides, а не дублируется отдельными source-фрагментами. + +Русский artifact дополнительно получает без изменений `README_RU.md` и содержательную пользовательскую документацию из `docs/ru/`. Локальный редакторский `guides/AGENTS.md`, а также навигационные `guides/README.md` и `reference/README.md` не копируются. Файлы находятся в `references/README_RU.md` и `references/docs/ru/`. Agent-specific `complex-svg.md` остаётся отдельным reference. + +Английский source пока сохраняет составную структуру с `core/`, а artifact — английские exact-mode guides и локальные `programmatic-api.md`/`complex-svg.md`. Его перевод на единый `SKILL.md` и полную canonical-документацию выполняется отдельно. ## Композиция Markdown -В любой собираемый документ можно включать фрагменты: +Сборщик сохраняет поддержку Markdown includes для английского skill и будущих документов: ```md diff --git a/skills/svg-sprites/build.mjs b/skills/svg-sprites/build.mjs index 6b421d9..00e8715 100644 --- a/skills/svg-sprites/build.mjs +++ b/skills/svg-sprites/build.mjs @@ -137,7 +137,13 @@ function expandCopies(config) { assertSafeRelativePath(entry.toDirectory) const sourceDirectory = path.resolve(skillDir, entry.fromDirectory) const extensions = entry.extensions ?? [] - for (const relativePath of listDirectoryFiles(sourceDirectory, extensions)) { + const sourceFiles = listDirectoryFiles(sourceDirectory, extensions) + const excluded = new Set((entry.exclude ?? []).map((relativePath) => { + assertSafeRelativePath(relativePath) + return relativePath.replaceAll('\\', '/') + })) + for (const relativePath of sourceFiles) { + if (excluded.has(relativePath)) continue copies.push({ from: path.join(sourceDirectory, relativePath), to: path.posix.join(entry.toDirectory, relativePath), diff --git a/skills/svg-sprites/skill.config.mjs b/skills/svg-sprites/skill.config.mjs index 642c338..c48ee2e 100644 --- a/skills/svg-sprites/skill.config.mjs +++ b/skills/svg-sprites/skill.config.mjs @@ -1,7 +1,12 @@ -const agentReferences = [ - 'programmatic-api.md', - 'complex-svg.md', -] +const agentReferences = { + en: [ + 'programmatic-api.md', + 'complex-svg.md', + ], + ru: [ + 'complex-svg.md', + ], +} const guideFiles = [ 'standalone.md', @@ -18,7 +23,7 @@ const guideFiles = [ function documents(language) { return [ { entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true }, - ...agentReferences.map((file) => ({ + ...agentReferences[language].map((file) => ({ entry: `src/${language}/references/${file}`, to: `references/${file}`, })), @@ -32,6 +37,20 @@ function guides(language) { })) } +const russianDocumentation = [ + { from: '../../README_RU.md', to: 'references/README_RU.md' }, + { + fromDirectory: '../../docs/ru', + toDirectory: 'references/docs/ru', + extensions: ['.md'], + exclude: [ + 'guides/AGENTS.md', + 'guides/README.md', + 'reference/README.md', + ], + }, +] + export default [ { name: 'svg-sprites', @@ -43,10 +62,10 @@ export default [ }, { name: 'svg-sprites-ru', - description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', + description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', output: '../artifacts/svg-sprites-ru', maxSkillBytes: 48_000, documents: documents('ru'), - copy: guides('ru'), + copy: russianDocumentation, }, ] diff --git a/skills/svg-sprites/src/ru/SKILL.md b/skills/svg-sprites/src/ru/SKILL.md index ffe0ca6..955a3ff 100644 --- a/skills/svg-sprites/src/ru/SKILL.md +++ b/skills/svg-sprites/src/ru/SKILL.md @@ -1,19 +1,282 @@ # @gromlab/svg-sprites - - - - - - - - +## Что делает пакет -## Справочники по необходимости +`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js. -- Для статической публикации открой [bare standalone](./references/guides/standalone.md). Для vanilla-приложений со сборщиком открой [standalone + Vite](./references/guides/standalone-vite.md) или [standalone + Webpack](./references/guides/standalone-webpack.md). -- Для React открой exact guide для [Vite](./references/guides/react-vite.md) или [Webpack](./references/guides/react-webpack.md). -- Для Next.js App Router открой exact guide для [Turbopack](./references/guides/next-app-turbopack.md) или [Webpack](./references/guides/next-app-webpack.md). -- Для Next.js Pages Router открой exact guide для [Turbopack](./references/guides/next-pages-turbopack.md) или [Webpack](./references/guides/next-pages-webpack.md). -- Для вызова генератора из Node.js открой [программный API](./references/programmatic-api.md). -- Для gradients, filters, `url(#...)`, нестандартных цветов и проблем с `viewBox` открой [сложные SVG](./references/complex-svg.md). +Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные: + +- SVG asset; +- mode-specific manifest data; +- для bundler modes — типы имён и production entry `.svg-sprite/index.js`; +- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации; +- только для React/Next.js — React-компонент; +- для bare `standalone` — deployment-neutral JSON manifest без публичного URL. + +Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Это примеры API отдельных спрайтов, а не фиксированные экспорты пакета. + +Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация через `npx --yes @gromlab/svg-sprites ` не добавляет package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API. + +## Выбор режима + +Выбери ровно один поддерживаемый mode key: + +| Проект | Mode key | +|---|---| +| Static HTML / собственная публикация | `standalone` | +| Standalone + Vite | `standalone@vite` | +| Standalone + Webpack 5 | `standalone@webpack` | +| React + Vite | `react@vite` | +| React + Webpack 5 | `react@webpack` | +| Next.js App Router + Turbopack | `next@app/turbopack` | +| Next.js App Router + Webpack 5 | `next@app/webpack` | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | +| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | + +Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен. + +`name` необязателен. Если он не задан, генератор преобразует имя каталога sprite-модуля в kebab-case; для каталогов `svg-sprite` и `svg-sprites` используется имя родительского каталога. Явное `name` должно уже быть записано в kebab-case и начинаться с латинской буквы. + +CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI. + +```json +{ + "scripts": { + "sprite:": "npx --yes @gromlab/svg-sprites ", + "sprite::cli": "npx --yes @gromlab/svg-sprites --mode " + } +} +``` + +Генерация через `npx` не добавляет package в проект. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. + +## Инспекция проекта + +До изменений установи фактический контракт проекта: + +1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды. +2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта. +3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд. +4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их. +5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения. +6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`. + +Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт: + +- `input?: string | string[]` по умолчанию равен `./icons`; +- каждая строка задаёт папку, точный SVG-файл или glob; +- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`; +- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора; +- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой; +- разрешённые файлы дедуплицируются и детерминированно сортируются; +- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников. + +Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения. + +## Настройка интеграции + +Не воспроизводи настройку mode по памяти. После инспекции проекта выбери один exact mode и открой соответствующий файл из `references/docs/ru/guides/`. Используй guide как базовый рабочий контракт, затем адаптируй его к существующей структуре проекта. + +Работай в таком порядке: + +1. Определи каталог исходных SVG и каталог одного sprite-модуля. Один config создаёт один независимый спрайт; для нескольких наборов нужны отдельные config-файлы и уникальные `name`. +2. Сверь framework, router и bundler с exact mode. Для Next.js проверяй реальные `dev` и `build` scripts, а не только наличие `next.config.*`. +3. Предпочитай JSON-конфиг, если проекту не нужны package-типы config. TypeScript-конфиг также загружается через CLI, но установка package нужна, когда он импортирует `defineSpriteConfig` или типы. +4. Разрешай все `input` относительно каталога config-файла. Не меняй структуру SVG без необходимости: используй путь к папке, точный файл, glob или массив этих источников. +5. Добавь sprite-команду с явным путём к config. Сохрани существующие `dev`, `build`, `typecheck` и lifecycle hooks; встрой генерацию до первого процесса, импортирующего `.svg-sprite`. +6. Не запускай одну генерацию дважды через одновременный `predev` и `npm run sprites && ...`. Для нескольких спрайтов создай отдельные команды и один агрегирующий script. +7. Если приложение импортирует каталог sprite-модуля, создай пользовательский `index.ts` рядом с `.svg-sprite`; не помещай пользовательские файлы внутрь generated-каталога. +8. Выполни первую генерацию до typecheck или запуска приложения, затем проверь mode-specific output и фактический импорт компонента. + +Не добавляй Viewer автоматически. Подключай его только по запросу пользователя или когда нужна визуальная проверка набора, цветов либо сложных SVG. Способ изоляции Viewer от production бери из exact guide: Vite, Webpack, App Router и Pages Router используют разные границы. + +Не копируй snippets между exact modes даже при похожем API. Различаются asset URL, generated-файлы, CSS handling, router boundary и способ подключения debug-инструментов. + +## Контракт generated-каталога + +После генерации React/Next-каталог имеет следующий вид: + +```text +svg-sprite/ +├── icons/ # пользовательские исходники +├── svg-sprite.config.json # рекомендуемое имя конфига +├── index.ts # необязательный пользовательский barrel +├── .gitignore # управляет генератор +└── .svg-sprite/ + ├── index.js + ├── index.d.ts + ├── icon-data.js + ├── icon-data.d.ts + ├── sprite.svg + ├── svg-sprite.manifest.js + ├── svg-sprite.manifest.d.ts + └── react/ + ├── react-component.js + ├── react-component.d.ts + └── react-component.module.css +``` + +Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также содержит нативный generated Web Component; bare `standalone` не получает JS runtime и не создаёт `.gitignore`. + +Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel: + +```ts +export * from './.svg-sprite/index.js' +``` + +Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт. Bare `standalone` сохраняет пользовательский `.gitignore`, но удаляет управляемый `.gitignore`, оставшийся после другого mode. Generated-пути не должны содержать symlink. + +В React/Next modes внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён, а `index.d.ts` добавляет props/style-типы и union имени. Standalone bundler modes экспортируют Web Component helpers и типы без `react/`; bare `standalone` не создаёт facade. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, target, список и метаданные иконок для debug-инструментов; bundler manifest также содержит URL и не импортируется production-компонентом. + +В bundler modes спрайт остаётся отдельным asset, а SVG path-данные не встраиваются в JavaScript. Content hash зависит от настроек сборщика. Bare `standalone` создаёт файл с фиксированным именем, а приложение само определяет его публичное имя и версионирование: + +- `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline; +- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React; +- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React; +- React Webpack 5 и все Next modes получают asset через `new URL(..., import.meta.url).href`, который должен обработать соответствующий сборщик; +- кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`; +- в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки; +- команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`. + +Для bundler modes не перемещай generated sprite в `public` и не переписывай URL вручную. Для bare `standalone` не перемещай managed original: приложение может явно копировать его в deploy output и само отвечает за публичный URL и очистку копии. При смене mode перегенерируй спрайт с новым полным key. + +## Использование, доступность и цвета + +Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `` и функцию `defineFileManagerIconElement()`: + +```ts +import { defineFileManagerIconElement } from './svg-sprite' + +defineFileManagerIconElement() +``` + +```html + +``` + +Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует. + +В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`. + +Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом. + +```tsx +import { FileManagerIcon } from './svg-sprite' + +export const OpenButton = () => ( + Открыть + +) +``` + +```css +.icon { + width: 24px; + height: 24px; + color: #4b5563; +} +``` + +`icon` принимает точные имена исходных файлов без `.svg`; неизвестное имя является ошибкой TypeScript. Для небезопасных SVG ID имён генератор хранит публичное имя, но создаёт внутренний стабильный hash ID, поэтому не собирай fragment URL из имени вручную. + +По умолчанию компонент рендерит `` и принимает стандартные SVG attributes: необязательные `width`/`height`, `className`, `style`, `role`, `aria-*` и обработчики. С `wrapped={true}` корнем становится ``, props относятся к span, а внутренний SVG занимает размер wrapper. + +Generated-компонент не выбирает семантику за приложение и не добавляет `title`. Для декоративной иконки передай `aria-hidden="true"`; для самостоятельной смысловой иконки передай `role="img"` и доступное имя через `aria-label`. Не дублируй имя, если соседний текст уже озвучивает действие. Интерактивность размещай на `button` или `a`, а не на самой иконке. + +Трансформации `removeSize`, `replaceColors` и `addTransition` включены по умолчанию. Для монохромной иконки единственный цвет получает fallback `currentColor`, поэтому управляй CSS-свойством `color`. Для многоцветной передавай типизированные custom properties: + +```tsx + +``` + +Автозамена рассчитана на `fill`/`stroke` attributes и inline `style`. Значения `none`, `transparent`, `inherit`, `unset`, `initial` не заменяются. CSS-классы и внешние stylesheets, gradients, patterns, filters и `url(#...)` проверяй на реальном результате. Переменные страницы работают через ``, но не проникают во внешний документ при `` или `background-image`; CSS mask оставляет только одноцветный силуэт. + +`SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer. Он принимает manifests или статически обнаружимые loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят. + +Перед подключением Viewer открой exact guide. Vite использует отдельную HTML entry, обычный Webpack — development-only entry, Next.js — debug route, а App Router дополнительно требует отдельную Client Component boundary. Не переноси способ подключения между modes. + +## Проверка результата + +После изменения конфига или SVG выполни обязательные проверки: + +1. Запусти точную sprite-команду. Процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`. +2. Проверь output выбранного exact mode: + - bare `standalone` создаёт `sprite.svg` и `svg-sprite.manifest.json`; + - `standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, `icon-data.*` и JS manifest, но не каталог `react/`; + - React и Next.js modes также создают `react/react-component.js`, declaration и CSS Module. +3. Для modes с public facade проверь `.svg-sprite/index.js`, соседний `index.d.ts`, список имён и фактический импорт через пользовательский barrel. +4. Проверь manifest: mode и target должны соответствовать выбранному adapter, а список иконок — исходным SVG. В bundler modes URL должен формироваться mode-specific способом; bare JSON manifest намеренно не содержит публичного `spriteUrl`. +5. Запусти существующий typecheck проекта, если mode создаёт типы или изменился пользовательский TypeScript-код. +6. Запусти минимальную команду приложения, затронутую изменением: `dev`, build или специализированную проверку проекта. + +Не запускай полную production-сборку только ради проверки нового имени иконки. Она нужна, если менялся bundler target, router, Webpack loader, asset URL, deployment path или диагностируется production-only ошибка. + +Визуальную проверку, Network и accessibility tree выполняй только при наличии запущенного приложения и браузерных инструментов. Если таких инструментов нет, не утверждай, что цвета, темы, доступность или HTTP-ответ asset проверены; явно укажи непроверенную часть. + +Viewer используй для сложных цветов, transforms и массовой визуальной проверки. Не добавляй debug route ради обычной генерации одного спрайта. + +## Диагностика + +Сопоставь симптом с проверкой и исправляй первопричину: + +| Симптом | Вероятная причина | Действие | +|---|---|---| +| `Missing sprite config file or module directory` | Не передан позиционный путь | Передай один config-файл либо каталог для config-less запуска. | +| `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. | +| `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. | +| `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. | +| Positive input-источник не нашёл SVG | Папка отсутствует или пуста, glob не совпал либо точный путь отсутствует или ведёт не к SVG | Разреши источник от каталога конфига и исправь `input`; каждый positive-элемент должен дать хотя бы один SVG. | +| Иконки из подпапки не появились | От папки ожидалось рекурсивное сканирование | Используй явный glob, например `./icons/**/*.svg`; папки сканируются плоско. | +| Исключённая иконка всё ещё присутствует | У исключения нет префикса `!`, оно находится не в массиве `input` или считается не от того каталога | Добавь совпадающий `!`-элемент и считай его от каталога конфига. | +| CLI выбрал не все источники | Несколько источников поместили в одно значение `--input` или пропустили option | Повтори `--input ` отдельно для каждого источника или исключения. | +| Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. | +| `Refusing to overwrite a user file` | В корне sprite-модуля уже есть пользовательский `.gitignore`, который mode должен создать | Не перезаписывай файл: выбери другой sprite-каталог или согласуй перенос существующего `.gitignore`. | +| Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Для bare `standalone` это ожидаемо; в остальных modes генерация не запускалась, barrel неверен либо type server держит старый модуль | Сверь exact mode, запусти sprite-команду, проверь `export * from './.svg-sprite/index.js'`, затем typecheck; при необходимости перезапусти TypeScript server. | +| SVG не загружается или URL неверен | Mode не совпадает со сборщиком, неверен Webpack `publicPath` либо кастомный loader перехватил asset | Сверь mode и build-команду, проверь Asset Modules/`publicPath`, исключи generated SVG из несовместимого loader. | +| Next build расходится между SSR и браузером | Модуль сгенерирован для другого bundler/router или URL переписан вручную | Верни generated `new URL(...)`, выбери точный Next mode и перегенерируй. | +| `color` не меняет многоцветную иконку | У иконки несколько переменных или она показана через ``/CSS background | Используй ``/`` и нужные `--icon-color-N`. | +| Gradient/filter выглядит неверно | Автозамена цветов не гарантирует сложные paint servers | Изучи generated SVG; при необходимости отключи `replaceColors` для спрайта или упрости источник. | +| Viewer пуст | Manifest не создан, loader не обнаружен сборщиком или неверна Client Component boundary | Сначала сгенерируй спрайт, затем сверь manifest import и способ подключения с exact guide; в App Router оставь `'use client'` только в компоненте Viewer. | + +При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к config-файлу или каталогу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и управляемый `.gitignore`. + +## Карта reference-документации + +References являются частью собранного skill. Открывай только документы, относящиеся к текущей задаче, но перед изменением интеграции exact-mode guide обязателен. + +### Обзор + +- [README пакета](./references/README_RU.md) — возможности, основной React/Next.js сценарий и ссылки на документацию. + +### Конфигурация + +- [Конфигурация](./references/docs/ru/configuration.md) — JSON, JavaScript, TypeScript, поля config, `input` и запуск CLI. + +### Exact-mode guides + +- [`standalone`](./references/docs/ru/guides/standalone.md) — static HTML и собственная публикация SVG. +- [`standalone@vite`](./references/docs/ru/guides/standalone-vite.md) — vanilla-приложение с Vite и Web Component. +- [`standalone@webpack`](./references/docs/ru/guides/standalone-webpack.md) — vanilla-приложение с Webpack 5 и Web Component. +- [`react@vite`](./references/docs/ru/guides/react-vite.md) — React с Vite. +- [`react@webpack`](./references/docs/ru/guides/react-webpack.md) — React с Webpack 5. +- [`next@app/turbopack`](./references/docs/ru/guides/next-app-turbopack.md) — Next.js App Router с Turbopack. +- [`next@app/webpack`](./references/docs/ru/guides/next-app-webpack.md) — Next.js App Router с Webpack. +- [`next@pages/turbopack`](./references/docs/ru/guides/next-pages-turbopack.md) — Next.js Pages Router с Turbopack. +- [`next@pages/webpack`](./references/docs/ru/guides/next-pages-webpack.md) — Next.js Pages Router с Webpack. + +### Технические справочники + +- [Технический справочник](./references/docs/ru/reference/technical.md) — requirements, CLI, naming, generated API, assets, transforms, цвета, Viewer, Git, CI и диагностика. +- [Программный API](./references/docs/ru/reference/programmatic-api.md) — `generateSprite`, overrides, config API, compiler и Viewer runtime. + +### Agent-specific reference + +- [Сложные SVG](./references/complex-svg.md) — gradients, patterns, filters, masks, `url(#...)`, `viewBox`, fragment IDs и визуальная диагностика. diff --git a/skills/svg-sprites/src/ru/core/00-package-overview.md b/skills/svg-sprites/src/ru/core/00-package-overview.md deleted file mode 100644 index ffa5f06..0000000 --- a/skills/svg-sprites/src/ru/core/00-package-overview.md +++ /dev/null @@ -1,16 +0,0 @@ -## Что делает пакет - -`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js. - -Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные: - -- SVG asset; -- mode-specific manifest data; -- для bundler modes — типы имён и production entry `.svg-sprite/index.js`; -- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации; -- только для React/Next.js — React-компонент; -- для bare `standalone` — deployment-neutral JSON manifest без публичного URL. - -Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Имена `FileManagerIcon` и `fileManagerIconNames` ниже являются примерами API одного из возможных спрайтов, а не фиксированными экспортами пакета. - -Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация работает через `npx --package` с зафиксированной версией без добавления package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API. diff --git a/skills/svg-sprites/src/ru/core/10-mode-selection.md b/skills/svg-sprites/src/ru/core/10-mode-selection.md deleted file mode 100644 index 1791654..0000000 --- a/skills/svg-sprites/src/ru/core/10-mode-selection.md +++ /dev/null @@ -1,30 +0,0 @@ -## Выбор режима - -Выбери ровно один поддерживаемый mode key: - -| Проект | Mode key | -|---|---| -| Static HTML / собственная публикация | `standalone` | -| Standalone + Vite | `standalone@vite` | -| Standalone + Webpack 5 | `standalone@webpack` | -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | - -Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен. - -CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI. - -```json -{ - "scripts": { - "sprite:": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites ", - "sprite::cli": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --mode " - } -} -``` - -Генерация через `npx` не добавляет package в проект. В CI укажи точную версию вместо `latest`. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога. diff --git a/skills/svg-sprites/src/ru/core/20-project-inspection.md b/skills/svg-sprites/src/ru/core/20-project-inspection.md deleted file mode 100644 index 9cfaecd..0000000 --- a/skills/svg-sprites/src/ru/core/20-project-inspection.md +++ /dev/null @@ -1,22 +0,0 @@ -## Инспекция проекта - -До изменений установи фактический контракт проекта: - -1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды. -2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта. -3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд. -4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их. -5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения. -6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`. - -Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт: - -- `input?: string | string[]` по умолчанию равен `./icons`; -- каждая строка задаёт папку, точный SVG-файл или glob; -- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`; -- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора; -- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой; -- разрешённые файлы дедуплицируются и детерминированно сортируются; -- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников. - -Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения. diff --git a/skills/svg-sprites/src/ru/core/30-react-next-setup.md b/skills/svg-sprites/src/ru/core/30-react-next-setup.md deleted file mode 100644 index 30b1714..0000000 --- a/skills/svg-sprites/src/ru/core/30-react-next-setup.md +++ /dev/null @@ -1,63 +0,0 @@ -## Настройка React или Next.js - -Выбери целевой каталог для одного спрайта. Он может находиться рядом с feature, в общем каталоге иконок или в любом другом месте, принятом в проекте. Следующая структура является только примером: - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Один `svg-sprite.config.ts` создаёт один независимый спрайт. Для нескольких наборов выбери несколько каталогов и дай каждому уникальное `name`. - -Генератор не нужно устанавливать в проект. Начни с plain config без package -import: - -```ts -export default { - mode: 'react@vite', - name: 'file-manager', - description: 'Иконки файлового менеджера', - input: ['./icons', '../../shared/icons/close.svg'], -} -``` - -`input` принимает одну папку, точный SVG-файл или glob либо массив, объединяющий эти источники. Элемент массива с префиксом `!` исключает совпадения. Папки сканируются плоско; для рекурсии нужен явный glob `**/*.svg`. Если `input` не задан, используется `./icons`. Все пути считаются от каталога конфига, и каждый positive-источник должен найти хотя бы один SVG. - -Контракт объекта одинаков для React и Next.js; отличается полный `mode`. Устанавливай package только для необязательного Viewer, программного API или package-типизации config. В exact guides также есть локальный copy-paste type для проектов без package. - -`name` должен начинаться с латинской буквы и записываться в kebab-case; из примера `file-manager` будут созданы `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`. Другой спрайт получает собственные имена. Если `name` не задан, генератор выводит его из каталога. - -Добавь отдельную команду с выбранным mode key и одним путём: - -```json -{ - "scripts": { - "sprite:file-manager": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts", - "sprites": "npm run sprite:file-manager" - } -} -``` - -Для Next.js укажи в config полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:` на каждый config-файл и последовательно вызови их из `sprites`. - -Чтобы задать источники через CLI, повторяй `--input `; значения образуют тот же массивный контракт, включая исключения с `!`: - -```bash -npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts \ - --input ./icons \ - --input '../../shared/icons/**/*.svg' \ - --input '!../../shared/icons/legacy-*.svg' -``` - -Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Generated declarations self-contained и не импортируют generator package. - -Запускай генерацию либо через `predev`/`prebuild`/`pretypecheck`, либо явно внутри соответствующих команд. Не используй обе формы для одной команды, иначе генерация выполнится дважды. Сохраняй существующие команды и не создавай второй одноимённый JSON key. - -Запусти первую генерацию вручную: - -```bash -npm run sprites -``` diff --git a/skills/svg-sprites/src/ru/core/40-generated-contract.md b/skills/svg-sprites/src/ru/core/40-generated-contract.md deleted file mode 100644 index 5561bfe..0000000 --- a/skills/svg-sprites/src/ru/core/40-generated-contract.md +++ /dev/null @@ -1,51 +0,0 @@ -## Контракт generated-каталога - -После генерации React/Next-каталог имеет следующий вид: - -```text -svg-sprite/ -├── icons/ # пользовательские исходники -├── svg-sprite.config.ts # рекомендуемое имя конфига -├── index.ts # необязательный пользовательский barrel -├── .gitignore # управляет генератор -└── .svg-sprite/ - ├── index.js - ├── index.d.ts - ├── icon-data.js - ├── icon-data.d.ts - ├── sprite.svg - ├── svg-sprite.manifest.js - ├── svg-sprite.manifest.d.ts - └── react/ - ├── react-component.js - ├── react-component.d.ts - └── react-component.module.css -``` - -Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и -`svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно -генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также -содержит нативный generated Web Component; bare `standalone` не получает JS runtime, -не создаёт и не изменяет `.gitignore`. - -Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel: - -```ts -export * from './.svg-sprite' -``` - -Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт; bare `standalone` оставляет существующий `.gitignore` без изменений. Generated-пути не должны содержать symlink. - -Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом. - -Спрайт остаётся отдельным asset с content hash; SVG path-данные не встраиваются в JavaScript: - -- `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline; -- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React; -- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React; -- React Webpack 5 и все Next modes генерируют `new URL('./sprite.svg', import.meta.url).href`, который должен обработать Asset Modules соответствующего сборщика; -- кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`; -- в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки; -- команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`. - -Для bundler modes не перемещай generated sprite в `public` и не переписывай URL вручную. Для bare `standalone` не перемещай managed original: приложение может явно копировать его в deploy output и само отвечает за публичный URL и очистку копии. При смене mode перегенерируй спрайт с новым полным key. diff --git a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md b/skills/svg-sprites/src/ru/core/50-usage-and-colors.md deleted file mode 100644 index 39d712e..0000000 --- a/skills/svg-sprites/src/ru/core/50-usage-and-colors.md +++ /dev/null @@ -1,87 +0,0 @@ -## Использование, доступность и цвета - -Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `` и функцию `defineFileManagerIconElement()`: - -```ts -import { defineFileManagerIconElement } from './svg-sprite' - -defineFileManagerIconElement() -``` - -```html - -``` - -Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует. - -В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`. - -Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом. - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenButton = () => ( - Открыть - -) -``` - -```css -.icon { - width: 24px; - height: 24px; - color: #4b5563; -} -``` - -`icon` принимает точные имена исходных файлов без `.svg`; неизвестное имя является ошибкой TypeScript. Для небезопасных SVG ID имён генератор хранит публичное имя, но создаёт внутренний стабильный hash ID, поэтому не собирай fragment URL из имени вручную. - -По умолчанию компонент рендерит `` и принимает стандартные SVG attributes: необязательные `width`/`height`, `className`, `style`, `role`, `aria-*` и обработчики. С `wrapped={true}` корнем становится ``, props относятся к span, а внутренний SVG занимает размер wrapper. Это удобно, когда размер и цвета полностью задаются классом: - -```tsx -