From aabb8986aab64003940d931e41652a3df3b20014 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Mon, 29 Jun 2026 19:29:19 +0530 Subject: [PATCH] Initial commit --- .example.env | 3 + .gitattributes | 2 + .gitguardian.yaml | 2 + .github/CODEOWNERS | 1 + .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug_report.yml | 75 + .github/ISSUE_TEMPLATE/config.yml | 7 + .github/ISSUE_TEMPLATE/feature_request.yml | 39 + .github/auto_assign.yml | 10 + .github/dependabot.yml | 25 + .github/pull_request_template.md | 25 + .github/secret_scanning.yml | 2 + .github/stale.yml | 21 + .github/workflows/auto-change-prs-branch.yml | 24 + .github/workflows/build-zip.yml | 19 + .../cancel-other-workflows-on-close.yml | 42 + .github/workflows/codeql.yml | 40 + .github/workflows/dependencies-auto-merge.yml | 30 + .github/workflows/e2e-modular.yml | 71 + .github/workflows/e2e.yml | 25 + .github/workflows/greetings.yml | 16 + .github/workflows/lint.yml | 17 + .github/workflows/prettier.yml | 25 + .gitignore | 25 + .husky/pre-commit | 1 + .npmrc | 2 + .nvmrc | 1 + .prettierignore | 2 + .prettierrc | 12 + LICENSE | 21 + README.md | 275 + UPDATE-PACKAGE-VERSIONS.md | 8 + bash-scripts/copy_env.sh | 8 + bash-scripts/set_global_env.sh | 91 + bash-scripts/update_version.sh | 17 + chrome-extension/manifest.ts | 84 + chrome-extension/package.json | 36 + chrome-extension/pre-build.tsconfig.json | 9 + chrome-extension/public/content.css | 0 chrome-extension/public/icon-128.png | Bin 0 -> 14500 bytes chrome-extension/public/icon-34.png | Bin 0 -> 6362 bytes chrome-extension/src/background/index.ts | 9 + chrome-extension/tsconfig.json | 10 + .../utils/plugins/make-manifest-plugin.ts | 83 + chrome-extension/vite.config.mts | 52 + eslint.config.ts | 105 + package.json | 97 + packages/dev-utils/README.md | 16 + packages/dev-utils/index.mts | 1 + packages/dev-utils/lib/index.ts | 2 + .../dev-utils/lib/manifest-parser/impl.ts | 39 + .../dev-utils/lib/manifest-parser/index.ts | 4 + .../dev-utils/lib/manifest-parser/types.ts | 5 + packages/dev-utils/lib/stream-file-to-zip.ts | 24 + packages/dev-utils/package.json | 29 + packages/dev-utils/tsconfig.json | 8 + packages/env/README.md | 31 + packages/env/index.mts | 9 + packages/env/lib/config.ts | 10 + packages/env/lib/const.ts | 4 + packages/env/lib/index.ts | 2 + packages/env/lib/types.ts | 13 + packages/env/package.json | 30 + packages/env/tsconfig.json | 8 + packages/env/use-env-example.png | Bin 0 -> 42432 bytes packages/hmr/index.mts | 1 + packages/hmr/lib/consts.ts | 6 + packages/hmr/lib/initializers/init-client.ts | 17 + .../lib/initializers/init-reload-server.ts | 53 + packages/hmr/lib/injections/refresh.ts | 33 + packages/hmr/lib/injections/reload.ts | 13 + packages/hmr/lib/interpreter/index.ts | 6 + packages/hmr/lib/plugins/index.ts | 3 + .../lib/plugins/make-entry-point-plugin.ts | 73 + .../hmr/lib/plugins/watch-public-plugin.ts | 13 + .../hmr/lib/plugins/watch-rebuild-plugin.ts | 68 + packages/hmr/lib/types.ts | 18 + packages/hmr/package.json | 36 + packages/hmr/rollup.config.ts | 29 + packages/hmr/tsconfig.json | 9 + packages/i18n/.gitignore | 1 + packages/i18n/README.md | 173 + packages/i18n/index.mts | 1 + packages/i18n/lib/consts.ts | 62 + packages/i18n/lib/i18n-dev.ts | 34 + packages/i18n/lib/i18n-prod.ts | 3 + packages/i18n/lib/index.ts | 4 + packages/i18n/lib/prepare-build.ts | 22 + .../i18n/lib/set-related-locale-import.ts | 38 + packages/i18n/lib/types.ts | 12 + packages/i18n/locales/en/messages.json | 45 + packages/i18n/locales/ko/messages.json | 45 + packages/i18n/package.json | 30 + packages/i18n/prepare-build.tsconfig.json | 4 + packages/i18n/tsconfig.json | 8 + packages/module-manager/README.md | 64 + packages/module-manager/index.mts | 3 + .../lib/base/cli-args-processor.ts | 51 + packages/module-manager/lib/base/index.ts | 2 + .../lib/base/run-module-manager.ts | 49 + packages/module-manager/lib/const.ts | 98 + packages/module-manager/lib/helpers/index.ts | 2 + packages/module-manager/lib/helpers/utils.ts | 113 + .../module-manager/lib/helpers/zip-utils.ts | 97 + packages/module-manager/lib/index.ts | 21 + packages/module-manager/lib/paths.ts | 6 + .../lib/processing/delete-feature.ts | 34 + .../module-manager/lib/processing/index.ts | 3 + .../lib/processing/modules-handler.ts | 48 + .../lib/processing/recover-feature.ts | 41 + packages/module-manager/lib/types.ts | 19 + packages/module-manager/package.json | 23 + packages/module-manager/tsconfig.json | 8 + packages/shared/README.md | 12 + packages/shared/const.ts | 3 + packages/shared/index.mts | 4 + packages/shared/lib/hoc/index.ts | 2 + .../shared/lib/hoc/with-error-boundary.tsx | 15 + packages/shared/lib/hoc/with-suspense.tsx | 10 + packages/shared/lib/hooks/index.ts | 1 + packages/shared/lib/hooks/use-storage.tsx | 56 + packages/shared/lib/utils/colorful-logger.ts | 27 + packages/shared/lib/utils/const.ts | 25 + packages/shared/lib/utils/helpers.ts | 8 + packages/shared/lib/utils/index.ts | 4 + .../shared/lib/utils/init-app-with-shadow.ts | 34 + packages/shared/lib/utils/types.ts | 10 + packages/shared/package.json | 33 + packages/shared/tsconfig.json | 8 + packages/storage/index.mts | 1 + packages/storage/lib/base/base.ts | 157 + packages/storage/lib/base/enums.ts | 42 + packages/storage/lib/base/index.ts | 3 + packages/storage/lib/base/types.ts | 54 + .../storage/lib/impl/example-theme-storage.ts | 28 + packages/storage/lib/impl/index.ts | 1 + packages/storage/lib/index.ts | 2 + packages/storage/lib/types.ts | 8 + packages/storage/package.json | 27 + packages/storage/tsconfig.json | 8 + packages/tailwindcss-config/package.json | 11 + .../tailwindcss-config/tailwind.config.ts | 8 + packages/tailwindcss-config/tsconfig.json | 7 + packages/tsconfig/app.json | 5 + packages/tsconfig/base.json | 25 + packages/tsconfig/module.json | 10 + packages/tsconfig/package.json | 7 + packages/ui/README.md | 336 + packages/ui/global.css | 20 + packages/ui/index.ts | 1 + packages/ui/lib/assets/warning.svg | 10 + packages/ui/lib/components/LoadingSpinner.tsx | 11 + packages/ui/lib/components/ToggleButton.tsx | 23 + .../components/error-display/ErrorDisplay.tsx | 13 + .../components/error-display/ErrorHeader.tsx | 21 + .../error-display/ErrorResetButton.tsx | 11 + .../error-display/ErrorStackTraceList.tsx | 20 + packages/ui/lib/components/index.ts | 3 + packages/ui/lib/index.ts | 3 + packages/ui/lib/utils.ts | 5 + packages/ui/lib/with-ui.ts | 7 + packages/ui/package.json | 37 + packages/ui/tailwind.config.ts | 7 + packages/ui/tsconfig.json | 13 + packages/vite-config/index.mts | 1 + .../vite-config/lib/build-content-script.ts | 96 + .../lib/get-content-script-entires.ts | 22 + packages/vite-config/lib/index.ts | 2 + packages/vite-config/lib/with-page-config.ts | 39 + packages/vite-config/package.json | 25 + packages/vite-config/tailwind.d.ts | 1 + packages/vite-config/tsconfig.json | 8 + packages/zipper/index.mts | 13 + packages/zipper/lib/index.ts | 1 + packages/zipper/lib/zip-bundle.ts | 87 + packages/zipper/package.json | 30 + packages/zipper/tsconfig.json | 8 + pages/content-runtime/README.md | 15 + pages/content-runtime/build.mts | 49 + pages/content-runtime/package.json | 32 + pages/content-runtime/src/matches/all/App.tsx | 9 + .../content-runtime/src/matches/all/index.css | 5 + .../content-runtime/src/matches/all/index.tsx | 5 + .../src/matches/example/App.tsx | 9 + .../src/matches/example/index.css | 5 + .../src/matches/example/index.tsx | 5 + pages/content-runtime/tailwind.config.ts | 5 + pages/content-runtime/tailwind.d.ts | 5 + pages/content-runtime/tsconfig.json | 12 + pages/content-ui/README.md | 23 + pages/content-ui/build.mts | 49 + pages/content-ui/package.json | 35 + pages/content-ui/public/logo.svg | 7 + pages/content-ui/src/matches/all/App.tsx | 18 + pages/content-ui/src/matches/all/index.css | 1 + pages/content-ui/src/matches/all/index.tsx | 5 + pages/content-ui/src/matches/example/App.tsx | 18 + .../content-ui/src/matches/example/index.css | 1 + .../content-ui/src/matches/example/index.tsx | 5 + pages/content-ui/tailwind.config.ts | 5 + pages/content-ui/tailwind.d.ts | 5 + pages/content-ui/tsconfig.json | 11 + pages/content/README.md | 18 + pages/content/build.mts | 39 + pages/content/package.json | 33 + pages/content/public/logo.svg | 7 + pages/content/src/matches/all/index.ts | 5 + pages/content/src/matches/example/index.ts | 5 + pages/content/src/sample-function.ts | 3 + pages/content/tsconfig.json | 11 + pages/devtools-panel/index.html | 12 + pages/devtools-panel/package.json | 39 + .../devtools-panel/public/logo_horizontal.svg | 124 + .../public/logo_horizontal_dark.svg | 117 + pages/devtools-panel/src/Panel.css | 28 + pages/devtools-panel/src/Panel.tsx | 45 + pages/devtools-panel/src/index.css | 1 + pages/devtools-panel/src/index.tsx | 15 + pages/devtools-panel/tailwind.config.ts | 5 + pages/devtools-panel/tsconfig.json | 11 + pages/devtools-panel/vite.config.mts | 17 + pages/devtools/index.html | 11 + pages/devtools/package.json | 29 + pages/devtools/public/logo.svg | 7 + pages/devtools/src/index.ts | 6 + pages/devtools/tsconfig.json | 11 + pages/devtools/vite.config.mts | 17 + pages/new-tab/index.html | 12 + pages/new-tab/package.json | 40 + pages/new-tab/public/logo_horizontal.svg | 124 + pages/new-tab/public/logo_horizontal_dark.svg | 117 + pages/new-tab/src/NewTab.css | 22 + pages/new-tab/src/NewTab.scss | 10 + pages/new-tab/src/NewTab.tsx | 31 + pages/new-tab/src/index.css | 1 + pages/new-tab/src/index.tsx | 15 + pages/new-tab/tailwind.config.ts | 5 + pages/new-tab/tsconfig.json | 11 + pages/new-tab/vite.config.mts | 17 + pages/options/index.html | 12 + pages/options/package.json | 39 + pages/options/public/logo_horizontal.svg | 124 + pages/options/public/logo_horizontal_dark.svg | 117 + pages/options/src/Options.css | 26 + pages/options/src/Options.tsx | 26 + pages/options/src/index.css | 1 + pages/options/src/index.tsx | 14 + pages/options/tailwind.config.ts | 5 + pages/options/tsconfig.json | 11 + pages/options/vite.config.mts | 17 + pages/popup/index.html | 12 + pages/popup/package.json | 39 + pages/popup/public/logo_vertical.svg | 124 + pages/popup/public/logo_vertical_dark.svg | 117 + pages/popup/src/Popup.css | 30 + pages/popup/src/Popup.tsx | 63 + pages/popup/src/index.css | 6 + pages/popup/src/index.tsx | 15 + pages/popup/tailwind.config.ts | 5 + pages/popup/tsconfig.json | 11 + pages/popup/vite.config.mts | 17 + pages/side-panel/index.html | 12 + pages/side-panel/package.json | 39 + pages/side-panel/public/logo_vertical.svg | 124 + .../side-panel/public/logo_vertical_dark.svg | 117 + pages/side-panel/src/SidePanel.css | 30 + pages/side-panel/src/SidePanel.tsx | 28 + pages/side-panel/src/index.css | 1 + pages/side-panel/src/index.tsx | 14 + pages/side-panel/tailwind.config.ts | 5 + pages/side-panel/tsconfig.json | 11 + pages/side-panel/vite.config.mts | 17 + pnpm-lock.yaml | 10095 ++++++++++++++++ pnpm-workspace.yaml | 13 + tests/e2e/config/wdio.browser.conf.ts | 58 + tests/e2e/config/wdio.conf.ts | 97 + tests/e2e/config/wdio.d.ts | 7 + tests/e2e/helpers/theme.ts | 26 + tests/e2e/package.json | 24 + tests/e2e/specs/page-content-runtime.test.ts | 36 + tests/e2e/specs/page-content-ui.test.ts | 21 + tests/e2e/specs/page-content.test.ts | 33 + tests/e2e/specs/page-devtools-panel.test.ts | 12 + tests/e2e/specs/page-new-tab.test.ts | 15 + tests/e2e/specs/page-options.test.ts | 13 + tests/e2e/specs/page-popup.test.ts | 12 + tests/e2e/specs/page-side-panel.test.ts | 12 + tests/e2e/specs/smoke.test.ts | 7 + tests/e2e/tsconfig.json | 12 + tests/e2e/utils/extension-path.ts | 49 + tsconfig.json | 11 + turbo.json | 57 + 292 files changed, 17976 insertions(+) create mode 100644 .example.env create mode 100644 .gitattributes create mode 100644 .gitguardian.yaml create mode 100644 .github/CODEOWNERS create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/auto_assign.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/secret_scanning.yml create mode 100644 .github/stale.yml create mode 100644 .github/workflows/auto-change-prs-branch.yml create mode 100644 .github/workflows/build-zip.yml create mode 100644 .github/workflows/cancel-other-workflows-on-close.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependencies-auto-merge.yml create mode 100644 .github/workflows/e2e-modular.yml create mode 100644 .github/workflows/e2e.yml create mode 100644 .github/workflows/greetings.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/prettier.yml create mode 100644 .gitignore create mode 100644 .husky/pre-commit create mode 100644 .npmrc create mode 100644 .nvmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 LICENSE create mode 100755 README.md create mode 100644 UPDATE-PACKAGE-VERSIONS.md create mode 100644 bash-scripts/copy_env.sh create mode 100644 bash-scripts/set_global_env.sh create mode 100644 bash-scripts/update_version.sh create mode 100644 chrome-extension/manifest.ts create mode 100644 chrome-extension/package.json create mode 100644 chrome-extension/pre-build.tsconfig.json create mode 100644 chrome-extension/public/content.css create mode 100644 chrome-extension/public/icon-128.png create mode 100644 chrome-extension/public/icon-34.png create mode 100644 chrome-extension/src/background/index.ts create mode 100644 chrome-extension/tsconfig.json create mode 100644 chrome-extension/utils/plugins/make-manifest-plugin.ts create mode 100644 chrome-extension/vite.config.mts create mode 100644 eslint.config.ts create mode 100644 package.json create mode 100644 packages/dev-utils/README.md create mode 100644 packages/dev-utils/index.mts create mode 100644 packages/dev-utils/lib/index.ts create mode 100644 packages/dev-utils/lib/manifest-parser/impl.ts create mode 100644 packages/dev-utils/lib/manifest-parser/index.ts create mode 100644 packages/dev-utils/lib/manifest-parser/types.ts create mode 100644 packages/dev-utils/lib/stream-file-to-zip.ts create mode 100644 packages/dev-utils/package.json create mode 100644 packages/dev-utils/tsconfig.json create mode 100644 packages/env/README.md create mode 100644 packages/env/index.mts create mode 100644 packages/env/lib/config.ts create mode 100644 packages/env/lib/const.ts create mode 100644 packages/env/lib/index.ts create mode 100644 packages/env/lib/types.ts create mode 100644 packages/env/package.json create mode 100644 packages/env/tsconfig.json create mode 100644 packages/env/use-env-example.png create mode 100644 packages/hmr/index.mts create mode 100644 packages/hmr/lib/consts.ts create mode 100644 packages/hmr/lib/initializers/init-client.ts create mode 100644 packages/hmr/lib/initializers/init-reload-server.ts create mode 100644 packages/hmr/lib/injections/refresh.ts create mode 100644 packages/hmr/lib/injections/reload.ts create mode 100644 packages/hmr/lib/interpreter/index.ts create mode 100644 packages/hmr/lib/plugins/index.ts create mode 100644 packages/hmr/lib/plugins/make-entry-point-plugin.ts create mode 100644 packages/hmr/lib/plugins/watch-public-plugin.ts create mode 100644 packages/hmr/lib/plugins/watch-rebuild-plugin.ts create mode 100644 packages/hmr/lib/types.ts create mode 100644 packages/hmr/package.json create mode 100644 packages/hmr/rollup.config.ts create mode 100644 packages/hmr/tsconfig.json create mode 100644 packages/i18n/.gitignore create mode 100644 packages/i18n/README.md create mode 100644 packages/i18n/index.mts create mode 100644 packages/i18n/lib/consts.ts create mode 100644 packages/i18n/lib/i18n-dev.ts create mode 100644 packages/i18n/lib/i18n-prod.ts create mode 100644 packages/i18n/lib/index.ts create mode 100644 packages/i18n/lib/prepare-build.ts create mode 100644 packages/i18n/lib/set-related-locale-import.ts create mode 100644 packages/i18n/lib/types.ts create mode 100644 packages/i18n/locales/en/messages.json create mode 100644 packages/i18n/locales/ko/messages.json create mode 100644 packages/i18n/package.json create mode 100644 packages/i18n/prepare-build.tsconfig.json create mode 100644 packages/i18n/tsconfig.json create mode 100644 packages/module-manager/README.md create mode 100644 packages/module-manager/index.mts create mode 100644 packages/module-manager/lib/base/cli-args-processor.ts create mode 100644 packages/module-manager/lib/base/index.ts create mode 100644 packages/module-manager/lib/base/run-module-manager.ts create mode 100644 packages/module-manager/lib/const.ts create mode 100644 packages/module-manager/lib/helpers/index.ts create mode 100644 packages/module-manager/lib/helpers/utils.ts create mode 100644 packages/module-manager/lib/helpers/zip-utils.ts create mode 100644 packages/module-manager/lib/index.ts create mode 100644 packages/module-manager/lib/paths.ts create mode 100644 packages/module-manager/lib/processing/delete-feature.ts create mode 100644 packages/module-manager/lib/processing/index.ts create mode 100644 packages/module-manager/lib/processing/modules-handler.ts create mode 100644 packages/module-manager/lib/processing/recover-feature.ts create mode 100644 packages/module-manager/lib/types.ts create mode 100644 packages/module-manager/package.json create mode 100644 packages/module-manager/tsconfig.json create mode 100644 packages/shared/README.md create mode 100644 packages/shared/const.ts create mode 100644 packages/shared/index.mts create mode 100644 packages/shared/lib/hoc/index.ts create mode 100644 packages/shared/lib/hoc/with-error-boundary.tsx create mode 100644 packages/shared/lib/hoc/with-suspense.tsx create mode 100644 packages/shared/lib/hooks/index.ts create mode 100644 packages/shared/lib/hooks/use-storage.tsx create mode 100644 packages/shared/lib/utils/colorful-logger.ts create mode 100644 packages/shared/lib/utils/const.ts create mode 100644 packages/shared/lib/utils/helpers.ts create mode 100644 packages/shared/lib/utils/index.ts create mode 100644 packages/shared/lib/utils/init-app-with-shadow.ts create mode 100644 packages/shared/lib/utils/types.ts create mode 100644 packages/shared/package.json create mode 100644 packages/shared/tsconfig.json create mode 100644 packages/storage/index.mts create mode 100644 packages/storage/lib/base/base.ts create mode 100644 packages/storage/lib/base/enums.ts create mode 100644 packages/storage/lib/base/index.ts create mode 100644 packages/storage/lib/base/types.ts create mode 100644 packages/storage/lib/impl/example-theme-storage.ts create mode 100644 packages/storage/lib/impl/index.ts create mode 100644 packages/storage/lib/index.ts create mode 100644 packages/storage/lib/types.ts create mode 100644 packages/storage/package.json create mode 100644 packages/storage/tsconfig.json create mode 100644 packages/tailwindcss-config/package.json create mode 100644 packages/tailwindcss-config/tailwind.config.ts create mode 100644 packages/tailwindcss-config/tsconfig.json create mode 100644 packages/tsconfig/app.json create mode 100644 packages/tsconfig/base.json create mode 100644 packages/tsconfig/module.json create mode 100644 packages/tsconfig/package.json create mode 100644 packages/ui/README.md create mode 100644 packages/ui/global.css create mode 100644 packages/ui/index.ts create mode 100644 packages/ui/lib/assets/warning.svg create mode 100644 packages/ui/lib/components/LoadingSpinner.tsx create mode 100644 packages/ui/lib/components/ToggleButton.tsx create mode 100644 packages/ui/lib/components/error-display/ErrorDisplay.tsx create mode 100644 packages/ui/lib/components/error-display/ErrorHeader.tsx create mode 100644 packages/ui/lib/components/error-display/ErrorResetButton.tsx create mode 100644 packages/ui/lib/components/error-display/ErrorStackTraceList.tsx create mode 100644 packages/ui/lib/components/index.ts create mode 100644 packages/ui/lib/index.ts create mode 100644 packages/ui/lib/utils.ts create mode 100644 packages/ui/lib/with-ui.ts create mode 100644 packages/ui/package.json create mode 100644 packages/ui/tailwind.config.ts create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/vite-config/index.mts create mode 100644 packages/vite-config/lib/build-content-script.ts create mode 100644 packages/vite-config/lib/get-content-script-entires.ts create mode 100644 packages/vite-config/lib/index.ts create mode 100644 packages/vite-config/lib/with-page-config.ts create mode 100644 packages/vite-config/package.json create mode 100644 packages/vite-config/tailwind.d.ts create mode 100644 packages/vite-config/tsconfig.json create mode 100644 packages/zipper/index.mts create mode 100644 packages/zipper/lib/index.ts create mode 100644 packages/zipper/lib/zip-bundle.ts create mode 100644 packages/zipper/package.json create mode 100644 packages/zipper/tsconfig.json create mode 100644 pages/content-runtime/README.md create mode 100644 pages/content-runtime/build.mts create mode 100644 pages/content-runtime/package.json create mode 100644 pages/content-runtime/src/matches/all/App.tsx create mode 100644 pages/content-runtime/src/matches/all/index.css create mode 100644 pages/content-runtime/src/matches/all/index.tsx create mode 100644 pages/content-runtime/src/matches/example/App.tsx create mode 100644 pages/content-runtime/src/matches/example/index.css create mode 100644 pages/content-runtime/src/matches/example/index.tsx create mode 100644 pages/content-runtime/tailwind.config.ts create mode 100644 pages/content-runtime/tailwind.d.ts create mode 100644 pages/content-runtime/tsconfig.json create mode 100644 pages/content-ui/README.md create mode 100644 pages/content-ui/build.mts create mode 100644 pages/content-ui/package.json create mode 100644 pages/content-ui/public/logo.svg create mode 100644 pages/content-ui/src/matches/all/App.tsx create mode 100644 pages/content-ui/src/matches/all/index.css create mode 100644 pages/content-ui/src/matches/all/index.tsx create mode 100644 pages/content-ui/src/matches/example/App.tsx create mode 100644 pages/content-ui/src/matches/example/index.css create mode 100644 pages/content-ui/src/matches/example/index.tsx create mode 100644 pages/content-ui/tailwind.config.ts create mode 100644 pages/content-ui/tailwind.d.ts create mode 100644 pages/content-ui/tsconfig.json create mode 100644 pages/content/README.md create mode 100644 pages/content/build.mts create mode 100644 pages/content/package.json create mode 100644 pages/content/public/logo.svg create mode 100644 pages/content/src/matches/all/index.ts create mode 100644 pages/content/src/matches/example/index.ts create mode 100644 pages/content/src/sample-function.ts create mode 100644 pages/content/tsconfig.json create mode 100644 pages/devtools-panel/index.html create mode 100644 pages/devtools-panel/package.json create mode 100644 pages/devtools-panel/public/logo_horizontal.svg create mode 100644 pages/devtools-panel/public/logo_horizontal_dark.svg create mode 100644 pages/devtools-panel/src/Panel.css create mode 100644 pages/devtools-panel/src/Panel.tsx create mode 100644 pages/devtools-panel/src/index.css create mode 100644 pages/devtools-panel/src/index.tsx create mode 100644 pages/devtools-panel/tailwind.config.ts create mode 100644 pages/devtools-panel/tsconfig.json create mode 100644 pages/devtools-panel/vite.config.mts create mode 100644 pages/devtools/index.html create mode 100644 pages/devtools/package.json create mode 100644 pages/devtools/public/logo.svg create mode 100644 pages/devtools/src/index.ts create mode 100644 pages/devtools/tsconfig.json create mode 100644 pages/devtools/vite.config.mts create mode 100644 pages/new-tab/index.html create mode 100644 pages/new-tab/package.json create mode 100644 pages/new-tab/public/logo_horizontal.svg create mode 100644 pages/new-tab/public/logo_horizontal_dark.svg create mode 100644 pages/new-tab/src/NewTab.css create mode 100644 pages/new-tab/src/NewTab.scss create mode 100644 pages/new-tab/src/NewTab.tsx create mode 100644 pages/new-tab/src/index.css create mode 100644 pages/new-tab/src/index.tsx create mode 100644 pages/new-tab/tailwind.config.ts create mode 100644 pages/new-tab/tsconfig.json create mode 100644 pages/new-tab/vite.config.mts create mode 100644 pages/options/index.html create mode 100644 pages/options/package.json create mode 100644 pages/options/public/logo_horizontal.svg create mode 100644 pages/options/public/logo_horizontal_dark.svg create mode 100644 pages/options/src/Options.css create mode 100644 pages/options/src/Options.tsx create mode 100644 pages/options/src/index.css create mode 100644 pages/options/src/index.tsx create mode 100644 pages/options/tailwind.config.ts create mode 100644 pages/options/tsconfig.json create mode 100644 pages/options/vite.config.mts create mode 100644 pages/popup/index.html create mode 100644 pages/popup/package.json create mode 100644 pages/popup/public/logo_vertical.svg create mode 100644 pages/popup/public/logo_vertical_dark.svg create mode 100644 pages/popup/src/Popup.css create mode 100644 pages/popup/src/Popup.tsx create mode 100644 pages/popup/src/index.css create mode 100644 pages/popup/src/index.tsx create mode 100644 pages/popup/tailwind.config.ts create mode 100644 pages/popup/tsconfig.json create mode 100644 pages/popup/vite.config.mts create mode 100644 pages/side-panel/index.html create mode 100644 pages/side-panel/package.json create mode 100644 pages/side-panel/public/logo_vertical.svg create mode 100644 pages/side-panel/public/logo_vertical_dark.svg create mode 100644 pages/side-panel/src/SidePanel.css create mode 100644 pages/side-panel/src/SidePanel.tsx create mode 100644 pages/side-panel/src/index.css create mode 100644 pages/side-panel/src/index.tsx create mode 100644 pages/side-panel/tailwind.config.ts create mode 100644 pages/side-panel/tsconfig.json create mode 100644 pages/side-panel/vite.config.mts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tests/e2e/config/wdio.browser.conf.ts create mode 100644 tests/e2e/config/wdio.conf.ts create mode 100644 tests/e2e/config/wdio.d.ts create mode 100644 tests/e2e/helpers/theme.ts create mode 100644 tests/e2e/package.json create mode 100644 tests/e2e/specs/page-content-runtime.test.ts create mode 100644 tests/e2e/specs/page-content-ui.test.ts create mode 100644 tests/e2e/specs/page-content.test.ts create mode 100644 tests/e2e/specs/page-devtools-panel.test.ts create mode 100644 tests/e2e/specs/page-new-tab.test.ts create mode 100644 tests/e2e/specs/page-options.test.ts create mode 100644 tests/e2e/specs/page-popup.test.ts create mode 100644 tests/e2e/specs/page-side-panel.test.ts create mode 100644 tests/e2e/specs/smoke.test.ts create mode 100644 tests/e2e/tsconfig.json create mode 100644 tests/e2e/utils/extension-path.ts create mode 100644 tsconfig.json create mode 100644 turbo.json diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..59aaabd --- /dev/null +++ b/.example.env @@ -0,0 +1,3 @@ +CEB_EXAMPLE=example_env +CEB_DEV_LOCALE= +CEB_CI= diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9fac9c2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto eol=lf +*.sh text eol=lf diff --git a/.gitguardian.yaml b/.gitguardian.yaml new file mode 100644 index 0000000..0e39617 --- /dev/null +++ b/.gitguardian.yaml @@ -0,0 +1,2 @@ +paths_ignore: + - "pnpm-lock.yaml" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..c50eb91 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Jonghakseo \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..e5ab532 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Jonghakseo diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..6f502c3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,75 @@ +name: Bug Report +description: Create a bug report. +title: "[Bug]: " +labels: [ "bug", "triage" ] +assignees: + - Jonghakseo + +body: + - type: textarea + id: to-reproduce + attributes: + label: To Reproduce + description: | + Steps to reproduce the behavior. + Use [Brie](https://go.briehq.com/github?utm_source=CEB) to capture screenshots, errors, and network activity for faster issue resolution. + placeholder: Describe the steps to reproduce the issue. + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + placeholder: Describe the expected behavior. + validations: + required: true + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + + - type: dropdown + id: desktop-os + attributes: + label: OS + description: Select your operating system. + options: [ Windows, macOS, Linux ] + default: 0 + validations: + required: true + + - type: dropdown + id: desktop-browser + attributes: + label: Browser + description: Select the browser where the issue occurs(Multiple choice). + multiple: true + options: [ Chrome, Opera, Opera GX, Firefox, Safari, Microsoft Edge, Brave ] + default: 0 + validations: + required: true + + - type: input + id: desktop-node-version + attributes: + label: Node Version + description: Specify the Node.js version you are using. + placeholder: 22.12.0 + + - type: input + id: desktop-other-packages + attributes: + label: Other Necessary Packages Version + description: Specify versions of other necessary packages. + placeholder: pnpm-10.10.0 + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context about the problem here. + placeholder: Provide additional details if necessary. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c978087 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: GitHub Community Support + url: https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/discussions + about: Please leave your questions here or on Discord - Link below "Security Bug Bounty" + - name: GitHub Security Bug Bounty + url: https://discord.gg/RhETcmdW + about: Please report security vulnerabilities here(Send DM to Jonghakseo or tell us on chat, you want to report it). diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..4e38f09 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,39 @@ +name: Feature Request +description: Suggest an idea for improvement. +title: "[Feature Request]: " +labels: [ "request-new-feature", "triage" ] +assignees: + - Jonghakseo + +body: + - type: textarea + id: problem-description + attributes: + label: Problem Description + description: A clear and concise description of what's the problem. Ex. I'm looking for a way to... + placeholder: Describe the problem or frustration. + validations: + required: true + + - type: textarea + id: proposed-solution + attributes: + label: Proposed Solution + description: A clear and concise description of what you want to happen. + placeholder: Describe the solution you'd like. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: A clear and concise description of any alternative solutions. + placeholder: Describe alternative solutions or features. + + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: Add any other context or screenshots about the feature request here. + placeholder: Provide additional details if necessary. diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml new file mode 100644 index 0000000..c9d20b5 --- /dev/null +++ b/.github/auto_assign.yml @@ -0,0 +1,10 @@ +addReviewers: true + +reviewers: + - Jonghakseo + +addAssignees: author + +filterLabels: + exclude: + - dependencies diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..eb69652 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,25 @@ +version: 2 +updates: + - package-ecosystem: "npm" + target-branch: 'dev' + directory: "/" + schedule: + interval: "daily" + labels: + - "dependencies" + - "npm" + groups: + dev-dependencies: + dependency-type: development + dependencies: + dependency-type: production + versioning-strategy: increase + + - package-ecosystem: "github-actions" + target-branch: 'dev' + directory: "/" + schedule: + interval: "daily" + labels: + - "dependencies" + - "github-actions" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..83c0ad9 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,25 @@ + + + + +> `*` denotes required fields + +## Priority* + +- [ ] High: This PR needs to be merged first, before other tasks. +- [x] Medium: This PR should be merged quickly to prevent conflicts due to common changes. (default) +- [ ] Low: This PR does not affect other tasks, so it can be merged later. + +## Purpose of the PR* + + +## Changes* + + +## How to check the feature + + + + +## Reference + diff --git a/.github/secret_scanning.yml b/.github/secret_scanning.yml new file mode 100644 index 0000000..2e81f9c --- /dev/null +++ b/.github/secret_scanning.yml @@ -0,0 +1,2 @@ +paths-ignore: + - "../pnpm-lock.yaml" diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..3c25536 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,21 @@ +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 90 +# Number of days of inactivity before a stale Issue or Pull Request is closed +daysUntilClose: 30 +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - pinned + - security +# Label to use when marking as stale +staleLabel: stale +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when removing the stale label. Set to `false` to disable +unmarkComment: false +# Comment to post when closing a stale Issue or Pull Request. Set to `false` to disable +closeComment: true +# Limit to only `issues` or `pulls` +only: issues diff --git a/.github/workflows/auto-change-prs-branch.yml b/.github/workflows/auto-change-prs-branch.yml new file mode 100644 index 0000000..b7ba9b5 --- /dev/null +++ b/.github/workflows/auto-change-prs-branch.yml @@ -0,0 +1,24 @@ +name: Make sure new PRs are sent to development + +on: + pull_request_target: + types: [ opened ] + +jobs: + check-branch: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: Vankka/pr-target-branch-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + target: main + exclude: dev + change-to: dev + comment: | + Your PR's base branch was set to `main`, PRs should be set to target `dev`. + The base branch of this PR has been automatically changed to `dev`, please check that there are no merge conflicts. + already-exists-action: close_this + already-exists-comment: "Closing, because {url} basing on the same branch" diff --git a/.github/workflows/build-zip.yml b/.github/workflows/build-zip.yml new file mode 100644 index 0000000..53d5d65 --- /dev/null +++ b/.github/workflows/build-zip.yml @@ -0,0 +1,19 @@ +name: Build And Upload Extension Zip Via Artifact + +on: [ pull_request_target ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: pnpm + - run: pnpm install --frozen-lockfile --prefer-offline + - run: pnpm build + - uses: actions/upload-artifact@v4 + with: + path: dist/* diff --git a/.github/workflows/cancel-other-workflows-on-close.yml b/.github/workflows/cancel-other-workflows-on-close.yml new file mode 100644 index 0000000..1d6f9c7 --- /dev/null +++ b/.github/workflows/cancel-other-workflows-on-close.yml @@ -0,0 +1,42 @@ +name: Cancel PR workflows on close +on: + pull_request_target: + types: [ closed ] + +jobs: + cancel: + runs-on: ubuntu-latest + permissions: + actions: write + if: ${{ github.event.pull_request.merged == false }} + steps: + - name: Cancel all queued/in_progress workflows for this PR + env: + BRANCH: ${{ github.event.pull_request.head.ref }} + run: | + owner="${GITHUB_REPOSITORY%%/*}" + repo="${GITHUB_REPOSITORY##*/}" + branch="$BRANCH" + token="${{ secrets.GITHUB_TOKEN }}" + + runs=$(curl -s -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $token" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$owner/$repo/actions/runs?branch=$branch&status=queued" | jq -r '.workflow_runs[].id') + + runs+=" " + runs+=$(curl -s -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $token" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$owner/$repo/actions/runs?branch=$branch&status=in_progress" | jq -r '.workflow_runs[].id') + + for run_id in $runs; do + if [ -n "$run_id" ]; then + echo "Cancelling workflow run: $run_id" + curl -s -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $token" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/$owner/$repo/actions/runs/$run_id/cancel" + fi + done diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bcbcf9e --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,40 @@ +name: "CodeQL Advanced" + +on: + push: + branches: [ "main", "dev" ] + pull_request: + branches: [ "main", "dev" ] + schedule: + - cron: '40 18 * * 0' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + security-events: write + packages: read + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependencies-auto-merge.yml b/.github/workflows/dependencies-auto-merge.yml new file mode 100644 index 0000000..0553b83 --- /dev/null +++ b/.github/workflows/dependencies-auto-merge.yml @@ -0,0 +1,30 @@ +name: Dependabot Pull Request Approve and Merge +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: dependabot-metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Approve a PR + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable auto-merge for Dependabot PRs + if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/e2e-modular.yml b/.github/workflows/e2e-modular.yml new file mode 100644 index 0000000..d473d23 --- /dev/null +++ b/.github/workflows/e2e-modular.yml @@ -0,0 +1,71 @@ +name: Modular E2E Tests Matrix + +on: [ pull_request_target ] + +permissions: + contents: read + +env: + ALL_SCENARIOS: '["content", "content-ui", "content-runtime", "background", "new-tab", "popup", "devtools", "side-panel", "options"]' + +jobs: + e2e-tests: + name: '${{ matrix.browser }} | Scenario: ${{ matrix.scenario }}' + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + browser: ['chrome', 'firefox'] + scenario: ['content', 'content-ui', 'content-runtime', 'background', 'new-tab', 'popup', 'devtools', 'side-panel', 'options'] + + env: + CEB_CI: true + CURRENT_SCENARIO: ${{ matrix.scenario }} + + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: pnpm + + - run: pnpm install --frozen-lockfile --prefer-offline + + - name: Configure modules | scenario "${{ matrix.scenario }}" + run: | + echo "Deleting all features except '${{ matrix.scenario }}'" + pnpm module-manager --de ${{ matrix.scenario }} tests + + - name: Run E2E tests (${{ matrix.browser }}) + run: pnpm e2e${{ matrix.browser == 'firefox' && ':firefox' || '' }} + + - name: Restore one random feature and re-run E2E tests + run: | + echo "ALL_SCENARIOS=$ALL_SCENARIOS" + echo "CURRENT_SCENARIO=$CURRENT_SCENARIO" + + scenarios=($(echo "$ALL_SCENARIOS" | jq -r '.[]')) + + # Filter out current scenario + filtered_scenarios=() + for scenario in "${scenarios[@]}"; do + if [ "$scenario" != "$CURRENT_SCENARIO" ]; then + filtered_scenarios+=("$scenario") + fi + done + + if [ ${#filtered_scenarios[@]} -eq 0 ]; then + echo "::warning::No other scenario available, skipping restoration." + exit 0 + fi + + random_feature="${filtered_scenarios[$RANDOM % ${#filtered_scenarios[@]}]}" + + echo "Restoring feature: $random_feature" + pnpm module-manager -r "$random_feature" + + - name: Run again E2E tests (${{ matrix.browser }}) + run: pnpm ${{ matrix.browser == 'chrome' && 'e2e' || 'e2e:firefox' }} + diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..0c1e70f --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,25 @@ +name: Run E2E Tests + +on: [ pull_request_target ] + +permissions: + contents: read + +jobs: + e2e: + name: E2E tests on ${{ matrix.browser }} + runs-on: ubuntu-latest + strategy: + matrix: + browser: [chrome, firefox] + env: + CEB_CI: true + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: pnpm + - run: pnpm install --frozen-lockfile --prefer-offline + - run: pnpm ${{ matrix.browser == 'chrome' && 'e2e' || 'e2e:firefox' }} diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..5435e78 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,16 @@ +name: Greetings + +on: [ pull_request_target, issues ] + +jobs: + greeting: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: 'Thank you for your contribution. We will check and reply to you as soon as possible.' + pr-message: 'Thank you for your contribution. We will check and reply to you as soon as possible.' \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..90279d9 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,17 @@ +name: Lint Check + +on: [ pull_request_target ] + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: pnpm + + - run: pnpm install --frozen-lockfile --prefer-offline + - run: pnpm lint diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 0000000..5c54081 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,25 @@ +name: Formating validation + +on: [ pull_request_target ] + +jobs: + prettier: + name: Prettier Check + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Run Prettier + id: prettier-run + uses: rutajdash/prettier-cli-action@v1.0.2 + with: + config_path: ./.prettierrc + file_pattern: "*.{js,jsx,ts,tsx,json}" + + - name: Prettier Output + if: ${{ failure() }} + shell: bash + run: | + echo "The following files aren't formatted properly:" + echo "${{steps.prettier-run.outputs.prettier_output}}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..707050d --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# dependencies +**/node_modules + +# testing +**/coverage + +# build +**/dist +**/build +**/dist-zip +chrome-extension/manifest.js +chrome-extension/pre-build.tsconfig.tsbuildinfo +tsconfig.tsbuildinfo + +# env +**/.env.* +**/.env + +# etc +.DS_Store +.idea +**/.turbo + +# compiled +**/tailwind-output.css diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..409035c --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm dlx lint-staged --allow-empty diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..2687d1f --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +public-hoist-pattern[]=@testing-library/dom +engine-strict=true \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..8320a6d --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.15.1 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..86b67dc --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +dist +*.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..3537583 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "trailingComma": "all", + "semi": true, + "singleQuote": true, + "arrowParens": "avoid", + "printWidth": 120, + "bracketSameLine": true, + "htmlWhitespaceSensitivity": "strict", + "plugins": [ + "prettier-plugin-tailwindcss" + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2c41808 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2025 Seo Jong Hak + +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/README.md b/README.md new file mode 100755 index 0000000..7ff88aa --- /dev/null +++ b/README.md @@ -0,0 +1,275 @@ +
+ + + + + Logo + + +![](https://img.shields.io/badge/React-61DAFB?style=flat-square&logo=react&logoColor=black) +![](https://img.shields.io/badge/Typescript-3178C6?style=flat-square&logo=typescript&logoColor=white) +![](https://badges.aleen42.com/src/vitejs.svg) + +![GitHub action badge](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/actions/workflows/build-zip.yml/badge.svg) +![GitHub action badge](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/actions/workflows/lint.yml/badge.svg) + + + +> This boilerplate +> has [Legacy version](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/tree/legacy) + +
+ +> [!NOTE] +> This project is listed in the [Awesome Vite](https://github.com/vitejs/awesome-vite) + +> [!TIP] +> Share storage state between all pages +> +> https://github.com/user-attachments/assets/3b8e189f-6443-490e-a455-4f9570267f8c + +## Table of Contents + +- [Intro](#intro) +- [Features](#features) +- [Structure](#structure) + - [ChromeExtension](#structure-chrome-extension) + - [Packages](#structure-packages) + - [Pages](#structure-pages) +- [Installation](#installation) + - [Chrome](#installation-chrome) + - [Firefox](#installation-firefox) +- [Install dependency](#install-dependency) + - [For root](#install-dependency-for-root) + - [For module](#install-dependency-for-module) +- [Environment variables](#env-variables) + - [Add new](#env-variables-new) + - [Set via CLI](#env-variables-cli-set) +- [Troubleshooting](#troubleshooting) + - [Hot module reload seems to have frozen](#hot-module-reload-seems-to-have-frozen) + - [Imports not resolving correctly](#imports-not-resolving-correctly) +- [Community](#community) +- [Debugging](#debugging) +- [Reference](#reference) +- [Star History](#star-history) +- [Contributors](#contributors) + +## Intro + +This boilerplate helps you create Chrome/Firefox extensions using React and Typescript. It improves +the build speed and development experience by using Vite and Turborepo. + +## Features + +- [React](https://reactjs.org/) +- [TypeScript](https://www.typescriptlang.org/) +- [Tailwindcss](https://tailwindcss.com/) +- [Vite](https://vitejs.dev/) with [Rollup](https://rollupjs.org/) +- [Turborepo](https://turbo.build/repo) +- [Prettier](https://prettier.io/) +- [ESLint](https://eslint.org/) +- [Chrome Extensions Manifest Version 3](https://developer.chrome.com/docs/extensions/mv3/intro/) +- [Custom i18n package](/packages/i18n/) +- [Custom HMR (Hot Module Rebuild) plugin](/packages/hmr) +- [End-to-end testing with WebdriverIO](https://webdriver.io/) + +## Installation + +1. Clone this repository.( ```git clone https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite``` ) +2. Ensure your node version is >= than in `.nvmrc` file, recommend to use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#intro) +3. Edit `/packages/i18n/locales/`{your locale(s)}/`messages.json` +4. In the objects `extensionDescription` and `extensionName`, change the `message` fields (leave `description` alone) +5. Install pnpm globally: `npm install -g pnpm` +6. Run `pnpm install` +7. Check if you have that configuration in your IDE/Editor: + - VS Code: + - Installed [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + - Installed [Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + - Enabled `Typescript Workbench version` in settings: + - CTRL + SHIFT + P -> Search: `Typescript: Select Typescript version...` -> `Use Workbench version` + - [Read more](https://code.visualstudio.com/docs/languages/typescript#_using-newer-typescript-versions) + - Optional, for imports to work correctly in WSL, you might need to install the [Remote - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) extension and connect to WSL remotely from VS Code. See overview section in the extension page for more information. + - WebStorm: + - Configured [ESLint](https://www.jetbrains.com/help/webstorm/eslint.html#ws_eslint_configure_run_eslint_on_save) + - Configured [Prettier](https://prettier.io/docs/en/webstorm.html) + - Optional, but useful `File | Settings | Tools | Actions on Save`\ + -> `Optimize imports` and `Reformat code` +8. Run `pnpm update-version ` for change the `version` to the desired version of your extension. + +> [!IMPORTANT] +> On Windows, make sure you have WSL enabled and Linux distribution (e.g. Ubuntu) installed on WSL. +> +> [Installation Guide](https://learn.microsoft.com/en-us/windows/wsl/install) + +Then, depending on the target browser: + +### For Chrome: + +1. Run: + - Dev: `pnpm dev` (on Windows, you should run as administrator; + see [issue#456](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/456)) + - Prod: `pnpm build` +2. Open in browser - `chrome://extensions` +3. Check - Developer mode +4. Click - Load unpacked in the upper left corner +5. Select the `dist` directory from the boilerplate project + +### For Firefox: + +1. Run: + - Dev: `pnpm dev:firefox` + - Prod: `pnpm build:firefox` +2. Open in browser - `about:debugging#/runtime/this-firefox` +3. Click - Load Temporary Add-on... in the upper right corner +4. Select the `./dist/manifest.json` file from the boilerplate project + +> [!NOTE] +> In Firefox, you load add-ons in temporary mode. That means they'll disappear after each browser close. You have to +> load the add-on on every browser launch. + +## Install dependency for turborepo: + +### For root: + +1. Run `pnpm i -w` + +### For module: + +1. Run `pnpm i -F ` + +`package` - Name of the package you want to install e.g. `nodemon` \ +`module-name` - You can find it inside each `package.json` under the key `name`, e.g. `@extension/content-script`, you +can use only `content-script` without `@extension/` prefix + +## How do I disable modules I'm not using? + +[Read here](packages/module-manager/README.md) + +## Environment variables + +Read: [Env Documentation](packages/env/README.md) + +## Boilerplate structure + +### Chrome extension + +The extension lives in the `chrome-extension` directory and includes the following files: + +- [`manifest.ts`](chrome-extension/manifest.ts) - script that outputs the `manifest.json` +- [`src/background`](chrome-extension/src/background) - [background script](https://developer.chrome.com/docs/extensions/mv3/background_pages/) + (`background.service_worker` in manifest.json) +- [`public`](chrome-extension/public/) - icons referenced in the manifest; content CSS for user's page injection + +> [!IMPORTANT] +> To facilitate development, the boilerplate is configured to "Read and change all your data on all websites". +> In production, it's best practice to limit the premissions to only the strictly necessary websites. See +> [Declaring permissions](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions) +> and edit `manifest.js` accordingly. + +### Pages + +Code that is transpiled to be part of the extension lives in the [pages](pages) directory. + +- [`content`](pages/content) - Scripts injected into specified pages (You can see it in console) +- [`content-ui`](pages/content-ui) - React Components injected into specified pages (You can see it at the very bottom of pages) +- [`content-runtime`](pages/content-runtime/src/) - [injected content scripts](https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts#functionality) + This can be injected from e.g. `popup` like standard `content` +- [`devtools`](pages/devtools/) - [extend the browser DevTools](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#creating) + (`devtools_page` in manifest.json) +- [`devtools-panel`](pages/devtools-panel/) - [DevTools panel](https://developer.chrome.com/docs/extensions/reference/api/devtools/panels) + for [devtools](pages/devtools/src/index.ts) +- [`new-tab`](pages/new-tab/) - [override the default New Tab page](https://developer.chrome.com/docs/extensions/develop/ui/override-chrome-pages) + (`chrome_url_overrides.newtab` in manifest.json) +- [`options`](pages/options/) - [options page](https://developer.chrome.com/docs/extensions/develop/ui/options-page) + (`options_page` in manifest.json) +- [`popup`](pages/popup/) - [popup](https://developer.chrome.com/docs/extensions/reference/api/action#popup) shown when + clicking the extension in the toolbar + (`action.default_popup` in manifest.json) +- [`side-panel`](pages/side-panel/) - [sidepanel (Chrome 114+)](https://developer.chrome.com/docs/extensions/reference/api/sidePanel) + (`side_panel.default_path` in manifest.json) + +### Packages + +Some shared packages: + +- `dev-utils` - utilities for Chrome extension development (manifest-parser, logger) +- `env` - exports object which contain all environment variables from `.env` and dynamically declared +- `hmr` - custom HMR plugin for Vite, injection script for reload/refresh, HMR dev-server +- `i18n` - custom internationalization package; provides i18n function with type safety and other validation +- `shared` - shared code for the entire project (types, constants, custom hooks, components etc.) +- `storage` - helpers for easier integration with [storage](https://developer.chrome.com/docs/extensions/reference/api/storage), e.g. local/session storages +- `tailwind-config` - shared Tailwind config for entire project +- `tsconfig` - shared tsconfig for the entire project +- `ui` - function to merge your Tailwind config with the global one; you can save components here +- `vite-config` - shared Vite config for the entire project + +Other useful packages: + +- `zipper` - run `pnpm zip` to pack the `dist` folder into `extension-YYYYMMDD-HHmmss.zip` inside the newly created + `dist-zip` +- `module-manager` - run `pnpm module-manager` to enable/disable modules +- `e2e` - run `pnpm e2e` for end-to-end tests of your zipped extension on different browsers + +## Troubleshooting + +### Hot module reload seems to have frozen + +If saving source files doesn't cause the extension HMR code to trigger a reload of the browser page, try this: + +1. Ctrl+C the development server and restart it (`pnpm run dev`) +2. If you get a [`grpc` error](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/612), + [kill the + `turbo` process](https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/612#issuecomment-2518982339) + and run `pnpm dev` again. + +### Imports not resolving correctly + +If you are using WSL and imports are not resolving correctly, ensure that you have connected VS Code to WSL remotely using the [Remote - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) extension. + +## Community + +To chat with other community members, you can join the [Discord](https://discord.gg/4ERQ6jgV9a) server. +You can ask questions on that server, and you can also help others. + +Also, suggest new features or share any challenges you've faced while developing Chrome extensions! + +## Debugging + +If you're debugging one, you can use [Brie](https://go.briehq.com/github?utm_source=CEB) lets you capture screenshots, errors, and network activity, making it easier for us to help. + +## Reference + +- [Chrome Extensions](https://developer.chrome.com/docs/extensions) +- [Vite Plugin](https://vitejs.dev/guide/api-plugin.html) +- [Rollup](https://rollupjs.org/guide/en/) +- [Turborepo](https://turbo.build/repo/docs) +- [Rollup-plugin-chrome-extension](https://www.extend-chrome.dev/rollup-plugin) + +## Star History + + + + + + Star History Chart + + + +## Contributors + +This Boilerplate is made possible thanks to all of its contributors. + + + All Contributors + + +--- + +## Special Thanks To + +| JetBrains Logo (Main) logo. | Jackson Hong | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +--- + +Made by [Jonghakseo](https://jonghakseo.github.io/) diff --git a/UPDATE-PACKAGE-VERSIONS.md b/UPDATE-PACKAGE-VERSIONS.md new file mode 100644 index 0000000..d160286 --- /dev/null +++ b/UPDATE-PACKAGE-VERSIONS.md @@ -0,0 +1,8 @@ +For update package version in all ```package.json``` files use this command in root: + +FOR WINDOWS YOU NEED TO USE E.G ```GIT BASH``` CONSOLE OR OTHER WHICH SUPPORT UNIX COMMANDS +```bash +pnpm update-version +``` + +If script was run successfully you will see ```Updated versions to ``` diff --git a/bash-scripts/copy_env.sh b/bash-scripts/copy_env.sh new file mode 100644 index 0000000..0c75866 --- /dev/null +++ b/bash-scripts/copy_env.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Check if .env does not exist and .example.env exists +if [ ! -f ".env" ] && [ -f ".example.env" ]; then + # Copy .example.env to .env + cp .example.env .env + echo ".example.env has been copied to .env" +fi diff --git a/bash-scripts/set_global_env.sh b/bash-scripts/set_global_env.sh new file mode 100644 index 0000000..0ced414 --- /dev/null +++ b/bash-scripts/set_global_env.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# Default values +CLI_CEB_DEV=false +CLI_CEB_FIREFOX=false + +validate_is_boolean() { + if [[ "$1" != "true" && "$1" != "false" ]]; then + echo "Invalid value for <$2>. Please use 'true' or 'false'." + exit 1 + fi +} + +# Validate if a key starts with CLI_CEB_ or CEB_ +validate_key() { + local key="$1" + local is_editable_section="${2:-false}" + + if [[ -n "$key" && ! "$key" =~ ^# ]]; then + if [[ "$is_editable_section" == true && ! "$key" =~ ^CEB_ ]]; then + echo "Invalid key: <$key>. All keys in the editable section must start with 'CEB_'." + exit 1 + elif [[ "$is_editable_section" == false && ! "$key" =~ ^CLI_CEB_ ]]; then + echo "Invalid key: <$key>. All CLI keys must start with 'CLI_CEB_'." + exit 1 + fi + fi +} + +parse_arguments() { + for arg in "$@" + do + key="${arg%%=*}" + value="${arg#*=}" + + validate_key "$key" + + case $key in + CLI_CEB_DEV) + CLI_CEB_DEV="$value" + validate_is_boolean "$CLI_CEB_DEV" "CLI_CEB_DEV" + ;; + CLI_CEB_FIREFOX) + CLI_CEB_FIREFOX="$value" + validate_is_boolean "$CLI_CEB_FIREFOX" "CLI_CEB_FIREFOX" + ;; + *) + cli_values+=("$key=$value") + ;; + esac + done +} + +# Validate keys in .env file +validate_env_keys() { + editable_section_starts=false + + while IFS= read -r line; do + key="${line%%=*}" + if [[ "$key" =~ ^CLI_CEB_ ]]; then + editable_section_starts=true + elif $editable_section_starts; then + validate_key "$key" true + fi + done < .env +} + +create_new_file() { + temp_file=$(mktemp) + + { + echo "# THOSE VALUES ARE EDITABLE ONLY VIA CLI" + echo "CLI_CEB_DEV=$CLI_CEB_DEV" + echo "CLI_CEB_FIREFOX=$CLI_CEB_FIREFOX" + for value in "${cli_values[@]}"; do + echo "$value" + done + echo "" + echo "# THOSE VALUES ARE EDITABLE" + + # Copy existing env values, without CLI section + grep -E '^CEB_' .env + } > "$temp_file" + + mv "$temp_file" .env +} + +# Main script execution +parse_arguments "$@" +validate_env_keys +create_new_file diff --git a/bash-scripts/update_version.sh b/bash-scripts/update_version.sh new file mode 100644 index 0000000..3bee333 --- /dev/null +++ b/bash-scripts/update_version.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Usage: ./update_version.sh +# FORMAT IS <0.0.0> + +if [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + find . -name 'package.json' -not -path '*/node_modules/*' -exec bash -c ' + # Parse the version from package.json + current_version=$(grep -o "\"version\": \"[^\"]*" "$0" | cut -d"\"" -f4) + + # Update the version + perl -i -pe"s/$current_version/'$1'/" "$0" + ' {} \; + + echo "Updated versions to $1"; +else + echo "Version format <$1> isn't correct, proper format is <0.0.0>"; +fi diff --git a/chrome-extension/manifest.ts b/chrome-extension/manifest.ts new file mode 100644 index 0000000..7b4ef87 --- /dev/null +++ b/chrome-extension/manifest.ts @@ -0,0 +1,84 @@ +import { readFileSync } from 'node:fs'; +import type { ManifestType } from '@extension/shared'; + +const packageJson = JSON.parse(readFileSync('./package.json', 'utf8')); + +/** + * @prop default_locale + * if you want to support multiple languages, you can use the following reference + * https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization + * + * @prop browser_specific_settings + * Must be unique to your extension to upload to addons.mozilla.org + * (you can delete if you only want a chrome extension) + * + * @prop permissions + * Firefox doesn't support sidePanel (It will be deleted in manifest parser) + * + * @prop content_scripts + * css: ['content.css'], // public folder + */ +const manifest = { + manifest_version: 3, + default_locale: 'en', + name: '__MSG_extensionName__', + browser_specific_settings: { + gecko: { + id: 'example@example.com', + strict_min_version: '109.0', + }, + }, + version: packageJson.version, + description: '__MSG_extensionDescription__', + host_permissions: [''], + permissions: ['storage', 'scripting', 'tabs', 'notifications', 'sidePanel'], + options_page: 'options/index.html', + background: { + service_worker: 'background.js', + type: 'module', + }, + action: { + default_popup: 'popup/index.html', + default_icon: 'icon-34.png', + }, + chrome_url_overrides: { + newtab: 'new-tab/index.html', + }, + icons: { + '128': 'icon-128.png', + }, + content_scripts: [ + { + matches: ['http://*/*', 'https://*/*', ''], + js: ['content/all.iife.js'], + }, + { + matches: ['https://example.com/*'], + js: ['content/example.iife.js'], + }, + { + matches: ['http://*/*', 'https://*/*', ''], + js: ['content-ui/all.iife.js'], + }, + { + matches: ['https://example.com/*'], + js: ['content-ui/example.iife.js'], + }, + { + matches: ['http://*/*', 'https://*/*', ''], + css: ['content.css'], + }, + ], + devtools_page: 'devtools/index.html', + web_accessible_resources: [ + { + resources: ['*.js', '*.css', '*.svg', 'icon-128.png', 'icon-34.png'], + matches: ['*://*/*'], + }, + ], + side_panel: { + default_path: 'side-panel/index.html', + }, +} satisfies ManifestType; + +export default manifest; diff --git a/chrome-extension/package.json b/chrome-extension/package.json new file mode 100644 index 0000000..1874b68 --- /dev/null +++ b/chrome-extension/package.json @@ -0,0 +1,36 @@ +{ + "name": "chrome-extension", + "version": "0.5.0", + "description": "chrome extension - core settings", + "type": "module", + "private": true, + "sideEffects": false, + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "ready": "tsc -b pre-build.tsconfig.json", + "build": "vite build", + "dev": "vite build --mode development", + "test": "vitest run", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "prettier": "prettier . --write --ignore-path ../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/env": "workspace:*", + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "webextension-polyfill": "^0.12.0" + }, + "devDependencies": { + "@extension/dev-utils": "workspace:*", + "@extension/hmr": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*", + "@laynezh/vite-plugin-lib-assets": "^1.1.0", + "magic-string": "^0.30.17", + "ts-loader": "^9.5.2" + } +} diff --git a/chrome-extension/pre-build.tsconfig.json b/chrome-extension/pre-build.tsconfig.json new file mode 100644 index 0000000..d70695e --- /dev/null +++ b/chrome-extension/pre-build.tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./", + "noEmit": false + }, + "include": ["manifest.ts"], + "exclude": [""] +} diff --git a/chrome-extension/public/content.css b/chrome-extension/public/content.css new file mode 100644 index 0000000..e69de29 diff --git a/chrome-extension/public/icon-128.png b/chrome-extension/public/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..d5bdad9464a61ff2c2c74b3cbe1f03d6ae23db04 GIT binary patch literal 14500 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 N^htfK3{A^_{tqm~E!qG8 literal 0 HcmV?d00001 diff --git a/chrome-extension/public/icon-34.png b/chrome-extension/public/icon-34.png new file mode 100644 index 0000000000000000000000000000000000000000..a741ff61ff6abea8fa984e04e6d3316c0d9c1920 GIT binary patch literal 6362 zcmds5c|4Te+rMX;7};i2_T@H6q9kQ0%V0tjC2Ntrl0Dg;vD~R_B_zs2$WoRvh_Yn& zM1*XO-Pj{ztHz$+@$~$jf8T%Jf8N*UGau*N_c_;fo$vL%zUSO0;=F+l`yT#15CpNG z(Ipu{5GrQphh+j!kZOfb;2(>Ju7wu_aZBv{P-l$97a@rE!5NZ7oI9AGbKt`QFRT9l{MfGPV}Sd^~dx61Fr|km7P{(mG}PRUJB?D8pN!)FVaM z&IR@&w`{apX5HwbmO4ZNe}z)X8PYIUY)Nwlls%XkKGzGJg)rbF_AJunM?{4Y=P%85 z?MDbO+_B^&U2|@8PMbtbAAg}tI}qd}Mu72_)!Qg2kwhfWm;@0SvHvMg%zFNrQ>_{- zI-yxpL+{+zzOS*bGzT~*eWLdto}5bPoyJbRaQrJvQC#u+?TM>l?rXkOQ`a~ntt3mU z%5%*pq6*uWJ4!ewx1<-E*Q6Idp7oz^B8qqe(m$(J#nEi@1A;=GiaH8p$MX?3f*rPIL@NY@3)PrAT>*?lGSd@$O!)C# zM&GB>x>bF%(>(^=+@{^i)_v9EZ{B&9hnQrv>u>!voLfz??1H%s5YXI&%TRxos^YLBfSZd+cRz9v@L7WXZ*E_i`b#*x#oA0>(R$sR4~N*`fv|VN(ooAfN9Wr?(tv0s3yT9bDIxJ{02R4jqP5` z4%v_-Cg5>l82K)vl|j*O6oDW=jT?r>zNg=ebqW-Xb*BBa;QzHieU!A8mb;*Qa_N1K z-{59Bl%P!%e6-<8>0tjJd1d2=zR6+76A!C!ErJ9>o?7l^$w>8b%4R#ywor?pi1kKH zmJIC(He4(u(dpx}AgK@3)Y!qkN%bIULeX-US>{x3UtpYGw?XB_rEjTa0Vm%olSm+g zN4`**ErB0=)rNtKnTX=V6>?TOC9mR#_A9UO@K=HqmxR)MHMiB({;=h~LBCfEsM1J8 zDSc|l*Dg5PCg4@NPApSnm0Vo0k#3pQ!IMM1(>Q!S5s|>*25($l&)+WZH`GnkBN)YxjlZuM!(!d= z4|~b^yO0skloP2xa6jJ}%U`Sm8ATvzhp49R`V)bN`kud2J3Ga*p{-zs;XX|ilv^Qc zR+mbbq9g1#eh$R*CydZMTOyh5Qng4ERVUGCnNUQ@ZvOeuQgvfn^YHj=`RcW@^7*8e zpw_^W*PX^)f*UfIXCJZbr}3l5!q+wiG6&y_epz`sU}C{rRXTmSu+4M4AV<%IGQ#&w zX25LztV<5t&N`b)76KICQjaLQ$8?HCoBk-t3RO>V&Ks}jQyaLMHa6}yV5WNHr){9i zn+Y>j;Q{Ae`C>4Kl)%l~ z<2NHFt$zqOU!J5;gGOeZ(N&`GLn*~~P4U-$)m|d$MXMY#3tqmMv9L9k;{K6m#rW-< zNkq;$L%!g%tCHsoEjCJB1I=X(^S9+z)DTCH1AmY%T}uxWt7mQO(C&S%sHfs)8oVT7 zv41om=H|IOmcQ1c#YU1-pQXl5tDO>`7IP^oEqrSdbhG_pL_j&~y3$@%el%CCDUrn8 zhRso3m;P93+NUh6_&795PCCRF8& z7zgUv+IlzsOj@?|ci4@J2t`Pld@%wcOZSb)V^6!^uYIth=uTZ$JmWMHlt{k*?y*2_ zMf>@j_|z(b_kKZNvg43jqHCYXKpg$eclplXwPLomO{$e|mus$50{!QytiYaeOK%6< z--aKDB7-u5d3(zhFU zKDgL7SQ>DNzuIwQWn=bIR?gKsgN+IjiQ2?;Q5c_f7={k%5YtC5n*T3s)w%{`h`P1q zP|3iCcT~Un+9`~ZlhRi@ysS@`OV$R()IFwZ+QC|CxOyT)w|!Y*n)6Pzkn-briR>XRfKb=AJAK| z)1N9RX1Dw-T*r*Tl(tUM2Cv_-{35(-N$LhCjTMa^e1Q`lpDL>^T`venAjO;~GZD{f zzxCV0YD4|IR>8qU(TS71mbgeoirJGpgzF?X3n0ah%$3e1*N85WU`QweQC&IOq7;~e zKLfM0SurNyXg)TLVga1L0Kr6Ku`oeds&$^luu%R*s&?smI5P@GniyT2bfWX&lF}Ja z6#+)VRq%q2Yt4pYoeG~{tKOwxvB%u)k8ZLkwpW^T3)fu3U=ml%J?x?wY~j1jMC8Qa zrsCwsSAFkYEAyX!=|)Ed=n!L^2107v`;rg5XP7V$Ad@_l*ZK) zAA9|rJ+6@1dK-ImG=U4}V86@@U_?k>^j#dzfg5=1d96w`1?^6TP0U5+t%lndSO{=a zX5gr&fzXeO7s!Z=DBP5+o%rVrmlvfmwXbgcd%rx)eVUFc3PoVAW!r^6 z=zKOqkeSFw5bWdDT4lGCjht*%A~zoApeL&Gq19{K!}2v3I^D|Hy#x( zeWAT)@D}7{t3qz4iL@-8`V@Y!{{4+TFL0>aPn5hrp90W$NTSgTfal8IpJgorGxwox z`!en|?(_iPY&?M}r~k6MAZ%BL(F3GL`rhK|d$4#w?450u7GI5B2RY{ip*%!t1d{G} zWq`=`r<^oYMx)*L)!7fKtTcufvllp$oAEfm=T}}x`Y=(Pqfumlf{j0LaUD>)Vvh4+ zo;ODQ)}p4`SF1eLU9UhzAk8?O(tlZ3+Y_dR#U}qlx`G#%$&fK{==7V)p0jBpO8H;p zFc<@}Qh@&@GLw0|XURiR*rR`MZO2uxa==m!0_8)AVW%fQkwq{d>SUM#9#zJ@@#b!a-weJ0 zzAL6cfb}_dhIwhT^{yC9+sClVSQ9w8y_GtaX75 zk#vZ(2Y)1{^zKmj_SewT^~GJN+wveu0xI6~EJ5%n8%REyS=%3Eo8=#f@3DfAgwbfp z-x$}Qq@!#&Q5v&7Ek7FCXx?-=n?^+kKldwSY;SCrFl64Azc`l+5;vR4w$Q3SP;=QN zczSWzn@}|VR(vl;Lqf^K-ZzecQxk=CUjv+1?!Hv=#woaV3fD+tFgZUxR+ozzY>Ngz73T}Z(IMUI zrXEE=wEQNIrZyDaN8tEB9bA$xGTU}!hTn4H92G!^825_^J9Vd^uFOo#)~kUjAI~`X znE53nO&3hY0D+{4er1&cMBCi>lFX13L5);C)M!Nddcn3z(Q zQNCV0!hJ}a7<1@1#TV;oSTl)bo_#Pn5DtW3Tpjnxkm1hlI*h7mOkpc5^Js?Ymio+^5tw_etK!wW$U60-K zTS+`FaT+E`MkB-_up=DQ`9cW6+s^3iM%^yg^m~vr9Uc6Fe?^tXqS0RNUopb|gaV*;{+TK zbLpE&k%%#+TcfesAgh9EMM;_%OvFxXe?y=BK~zEStRy;wwB@uorSy^`eUDgO*HjKr z(Srs;q<8>?96F={6pr}LJLbNFazj`w*F|h}JJ7;wY)i%d>v){_0TD(%H3il76_izr z7_8gy8}01RT`rHmG6CyzEZcEO)7cG>VN%5%1oruPOtlt-2fC+#-uvtbcKi7V0|mWX ztm?htR=-unh!Xa)bx4bd%WJJI14uO-GcN~auJLNmsGM7hy|R#s>-+(9Nbf}E@s?IE znQ-2pc^Jq|Yb^K)$6(v@ro% z^^R$}q>;G3SX>r{#lHH7@Hq`SKB^?5O;7%!jAFZIMLobo63tc1IU?)YnId`))YW}n zeC!drD2@|6zJK{9Pw_R~I3PF*1`Txc@?~CBgqsl=IsWh2MVCiG?}s2NV*UXGaytKa zE)ZkBM{iLH`6}SVHSR?xJmMr%o!b%P8IX}BF&JN2D&eb5;&5(%?&Rh1FloB952WTP zJEq~`5euaOI1U7%$VYSfJ_p0x@4?uazYdoP5pL>|6EcBBVP0~KolH?^f0|}_#JQao z|E)drR;2C~@0hzP!oPItNA7N`WT=LnkXRE-d=lSpuasjD`S(eL>5&!@N%~o04ek@b z<7}NhTP|HGax%6k#UCZU*rlVjohI6N98KDvjuPw?#ut;A%sG$-ZPI%-?uJ%o%8lB7=}uik_B{or79D4)o!2;cja&cJ!>LTNB$R07*ZFZhnRco~D5zQ5eR zE7O=qKq8$V%xhTgdG<%<=aJW{m_bcq%wg6r9eR(jn4@`l2p%2sYIJtoK*f~=V<{D_wYc*pn=1yHEDGlyU; z7n6|Vzz92Qi`kiH6~l?2Hz42BMCjC`@^!yIt$bp+K|ErqHDoq`ekb^D@M#F#4OCcH z2#qH7P=`ec&h|l1fH;!+aX7fVl%n>BU%#LeiHLk%ma9_GH`6@~26j-Zv`F*OOF}0B zo19J)k?SYz1L#^Rt`Cok!NlX9L)*Muv36%E?B-x;7nhD$dwyUxibkNRWx2|EjTD72 zRxH*gUp|!TkU>%cMR#CKx!lL-k$MO2D1b%Yb2D9bxmej!6SVzXD2>_?9vmoCuH*I` zXF@(r9E~<^XULKPOC<5Qv_BMf-FzCwHF^j+jw40^BwWA6Z0%=&15YBDnq^WEVB#>d$KvnyUK~i<6b@%2 z*CS+gN{V4{2$QTqME)|_i)mBPIe^F6c&_cK&Ij>IjtgRRL12UV%Wu@R?;2W()?=|A zFZG^KKz+Wz<7(IhBnaQJ*z=VDC|@T|)O8TtvvSALj57V1vsc+*urYg7+sj*{x1W(h3}SOIi+zovU5`|no#h6;wpWc(~dnQp!i|U3IzVnoHiik IYubeT2YJB^CIA2c literal 0 HcmV?d00001 diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts new file mode 100644 index 0000000..dcd4d36 --- /dev/null +++ b/chrome-extension/src/background/index.ts @@ -0,0 +1,9 @@ +import 'webextension-polyfill'; +import { exampleThemeStorage } from '@extension/storage'; + +exampleThemeStorage.get().then(theme => { + console.log('theme', theme); +}); + +console.log('Background loaded'); +console.log("Edit 'chrome-extension/src/background/index.ts' and save to reload."); diff --git a/chrome-extension/tsconfig.json b/chrome-extension/tsconfig.json new file mode 100644 index 0000000..52c98ad --- /dev/null +++ b/chrome-extension/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@extension/tsconfig/app", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + } + }, + "include": ["src", "utils", "vite.config.mts", "manifest.ts", "../node_modules/@types"] +} diff --git a/chrome-extension/utils/plugins/make-manifest-plugin.ts b/chrome-extension/utils/plugins/make-manifest-plugin.ts new file mode 100644 index 0000000..ea0a9e3 --- /dev/null +++ b/chrome-extension/utils/plugins/make-manifest-plugin.ts @@ -0,0 +1,83 @@ +import { ManifestParser } from '@extension/dev-utils'; +import { IS_DEV, IS_FIREFOX } from '@extension/env'; +import { colorfulLog } from '@extension/shared'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { platform } from 'node:process'; +import { pathToFileURL } from 'node:url'; +import type { ManifestType } from '@extension/shared'; +import type { PluginOption } from 'vite'; + +const manifestFile = resolve(import.meta.dirname, '..', '..', 'manifest.js'); +const refreshFilePath = resolve( + import.meta.dirname, + '..', + '..', + '..', + 'packages', + 'hmr', + 'dist', + 'lib', + 'injections', + 'refresh.js', +); + +const withHMRId = (code: string) => `(function() {let __HMR_ID = 'chrome-extension-hmr';${code}\n})();`; + +const getManifestWithCacheBurst = async () => { + const withCacheBurst = (path: string) => `${path}?${Date.now().toString()}`; + + /** + * In Windows, import() doesn't work without file:// protocol. + * So, we need to convert path to file:// protocol. (url.pathToFileURL) + */ + if (platform === 'win32') { + return (await import(withCacheBurst(pathToFileURL(manifestFile).href))).default; + } else { + return (await import(withCacheBurst(manifestFile))).default; + } +}; + +const addRefreshContentScript = (manifest: ManifestType) => { + manifest.content_scripts = manifest.content_scripts || []; + manifest.content_scripts.push({ + matches: ['http://*/*', 'https://*/*', ''], + js: ['refresh.js'], // for public's HMR(refresh) support + }); +}; + +export default (config: { outDir: string }): PluginOption => { + const makeManifest = (manifest: ManifestType, to: string) => { + if (!existsSync(to)) { + mkdirSync(to); + } + + const manifestPath = resolve(to, 'manifest.json'); + + if (IS_DEV) { + addRefreshContentScript(manifest); + } + + writeFileSync(manifestPath, ManifestParser.convertManifestToString(manifest, IS_FIREFOX)); + + const refreshFileString = readFileSync(refreshFilePath, 'utf-8'); + + if (IS_DEV) { + writeFileSync(resolve(to, 'refresh.js'), withHMRId(refreshFileString)); + } + + colorfulLog(`Manifest file copy complete: ${manifestPath}`, 'success'); + }; + + return { + name: 'make-manifest', + buildStart() { + this.addWatchFile(manifestFile); + }, + async writeBundle() { + const outDir = config.outDir; + const manifest = await getManifestWithCacheBurst(); + makeManifest(manifest, outDir); + }, + }; +}; diff --git a/chrome-extension/vite.config.mts b/chrome-extension/vite.config.mts new file mode 100644 index 0000000..09f7ea2 --- /dev/null +++ b/chrome-extension/vite.config.mts @@ -0,0 +1,52 @@ +import { resolve } from 'node:path'; +import { defineConfig, type PluginOption } from 'vite'; +import libAssetsPlugin from '@laynezh/vite-plugin-lib-assets'; +import makeManifestPlugin from './utils/plugins/make-manifest-plugin.js'; +import { watchPublicPlugin, watchRebuildPlugin } from '@extension/hmr'; +import { watchOption } from '@extension/vite-config'; +import env, { IS_DEV, IS_PROD } from '@extension/env'; +import { nodePolyfills } from 'vite-plugin-node-polyfills'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +const outDir = resolve(rootDir, '..', 'dist'); +export default defineConfig({ + define: { + 'process.env': env, + }, + resolve: { + alias: { + '@root': rootDir, + '@src': srcDir, + '@assets': resolve(srcDir, 'assets'), + }, + }, + plugins: [ + libAssetsPlugin({ + outputPath: outDir, + }) as PluginOption, + watchPublicPlugin(), + makeManifestPlugin({ outDir }), + IS_DEV && watchRebuildPlugin({ reload: true, id: 'chrome-extension-hmr' }), + nodePolyfills(), + ], + publicDir: resolve(rootDir, 'public'), + build: { + lib: { + name: 'BackgroundScript', + fileName: 'background', + formats: ['es'], + entry: resolve(srcDir, 'background', 'index.ts'), + }, + outDir, + emptyOutDir: false, + sourcemap: IS_DEV, + minify: IS_PROD, + reportCompressedSize: IS_PROD, + watch: watchOption, + rollupOptions: { + external: ['chrome'], + }, + }, +}); diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..de172c4 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,105 @@ +import { fixupConfigRules } from '@eslint/compat'; +import { FlatCompat } from '@eslint/eslintrc'; +import js from '@eslint/js'; +import { flatConfigs as importXFlatConfig } from 'eslint-plugin-import-x'; +import jsxA11y from 'eslint-plugin-jsx-a11y'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import reactPlugin from 'eslint-plugin-react'; +import { browser, es2020, node } from 'globals'; +import { config, configs as tsConfigs, parser as tsParser } from 'typescript-eslint'; +import type { FixupConfigArray } from '@eslint/compat'; + +export default config( + // Shared configs + js.configs.recommended, + ...tsConfigs.recommended, + jsxA11y.flatConfigs.recommended, + importXFlatConfig.recommended, + importXFlatConfig.typescript, + eslintPluginPrettierRecommended, + ...fixupConfigRules(new FlatCompat().extends('plugin:react-hooks/recommended') as FixupConfigArray), + { + files: ['**/*.{ts,tsx}'], + ...reactPlugin.configs.flat.recommended, + ...reactPlugin.configs.flat['jsx-runtime'], + }, + // Custom config + { + ignores: ['**/build/**', '**/dist/**', '**/node_modules/**', 'chrome-extension/manifest.js'], + }, + { + files: ['**/*.{ts,tsx}'], + languageOptions: { + parser: tsParser, + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + ecmaFeatures: { jsx: true }, + projectService: true, + }, + globals: { + ...browser, + ...es2020, + ...node, + chrome: 'readonly', + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + 'prefer-const': 'error', + 'no-var': 'error', + 'func-style': ['error', 'expression', { allowArrowFunctions: true }], + 'no-restricted-imports': [ + 'error', + { + name: 'type-fest', + message: 'Please import from `@extension/shared` instead of `type-fest`.', + }, + ], + 'arrow-body-style': ['error', 'as-needed'], + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/consistent-type-exports': 'error', + 'import-x/order': [ + 'error', + { + 'newlines-between': 'never', + alphabetize: { order: 'asc', caseInsensitive: true }, + groups: ['index', 'sibling', 'parent', 'internal', 'external', 'builtin', 'object', 'type'], + pathGroups: [ + { + pattern: '@*/**', + group: 'internal', + position: 'before', + }, + ], + pathGroupsExcludedImportTypes: ['type'], + }, + ], + 'import-x/no-unresolved': 'off', + 'import-x/no-named-as-default': 'error', + 'import-x/no-named-as-default-member': 'error', + 'import-x/newline-after-import': 'error', + 'import-x/no-deprecated': 'error', + 'import-x/no-duplicates': ['error', { considerQueryString: true, 'prefer-inline': false }], + 'import-x/consistent-type-specifier-style': 'error', + 'import-x/exports-last': 'error', + 'import-x/first': 'error', + }, + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, + }, + // Overrides Rules + { + files: ['**/packages/shared/**/*.ts'], + rules: { + 'no-restricted-imports': 'off', + }, + }, +); diff --git a/package.json b/package.json new file mode 100644 index 0000000..5bc2b81 --- /dev/null +++ b/package.json @@ -0,0 +1,97 @@ +{ + "name": "chrome-extension-boilerplate-react-vite", + "version": "0.5.0", + "description": "chrome extension boilerplate", + "license": "MIT", + "private": true, + "sideEffects": false, + "repository": { + "type": "git", + "url": "https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite.git" + }, + "type": "module", + "scripts": { + "clean:bundle": "rimraf dist && turbo clean:bundle", + "clean:node_modules": "pnpx rimraf node_modules && pnpx turbo clean:node_modules", + "clean:turbo": "rimraf .turbo && turbo clean:turbo", + "clean": "pnpm clean:bundle && pnpm clean:turbo && pnpm clean:node_modules", + "clean:install": "pnpm clean:node_modules && pnpm install --frozen-lockfile", + "type-check": "turbo type-check", + "base-build": "pnpm clean:bundle && turbo build", + "build": "pnpm set-global-env && pnpm base-build", + "build:firefox": "pnpm set-global-env CLI_CEB_FIREFOX=true && pnpm base-build", + "base-dev": "pnpm clean:bundle && turbo ready && turbo watch dev", + "dev": "pnpm set-global-env CLI_CEB_DEV=true && pnpm base-dev", + "dev:firefox": "pnpm set-global-env CLI_CEB_DEV=true CLI_CEB_FIREFOX=true && pnpm base-dev", + "zip": "pnpm build && pnpm -F zipper zip", + "zip:firefox": "pnpm build:firefox && pnpm -F zipper zip", + "e2e": "pnpm zip && turbo e2e", + "e2e:firefox": "pnpm zip:firefox && turbo e2e", + "lint": "turbo lint --continue", + "lint:fix": "turbo lint:fix --continue", + "format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache", + "prepare": "husky", + "update-version": "bash bash-scripts/update_version.sh", + "copy-env": "bash bash-scripts/copy_env.sh", + "set-global-env": "bash bash-scripts/set_global_env.sh", + "module-manager": "pnpm -F module-manager start", + "postinstall": "pnpm copy-env" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.27.0", + "@types/chrome": "^0.0.323", + "@types/eslint-plugin-jsx-a11y": "^6.10.0", + "@types/node": "^22.15.21", + "@types/react": "^19.1.5", + "@types/react-dom": "^19.1.5", + "@typescript-eslint/parser": "^8.32.1", + "autoprefixer": "^10.4.21", + "cross-env": "^7.0.3", + "deepmerge": "^4.3.1", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-import-resolver-typescript": "^4.3.5", + "eslint-plugin-import-x": "^4.12.2", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-prettier": "^5.4.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-tailwindcss": "^3.18.0", + "fast-glob": "^3.3.3", + "fflate": "^0.8.2", + "globals": "^16.1.0", + "husky": "^9.1.7", + "jiti": "^2.4.2", + "lint-staged": "^16.0.0", + "postcss": "^8.5.3", + "postcss-load-config": "^6.0.1", + "prettier": "^3.5.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "rimraf": "^6.0.1", + "run-script-os": "^1.1.6", + "tailwindcss": "^3.4.17", + "tslib": "^2.8.1", + "tsx": "^4.19.4", + "turbo": "^2.5.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.32.1", + "vite": "^6.3.6", + "vite-plugin-node-polyfills": "^0.23.0" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx,json}": [ + "prettier --write", + "eslint --fix" + ] + }, + "packageManager": "pnpm@10.11.0", + "engines": { + "node": ">=22.15.1" + } +} diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md new file mode 100644 index 0000000..e71eba0 --- /dev/null +++ b/packages/dev-utils/README.md @@ -0,0 +1,16 @@ +# Shared Package + +This package contains code which could helps you to develop. +To use the code in the package, you need to add the following to the package.json file. + +```json +{ + "devDependencies": { + "@extension/dev-utils": "workspace:*" + } +} +``` + +## Additional Information + +`lib/shared-types.ts` contains the types from [type-fest](https://github.com/sindresorhus/type-fest?tab=readme-ov-file#install). \ No newline at end of file diff --git a/packages/dev-utils/index.mts b/packages/dev-utils/index.mts new file mode 100644 index 0000000..09c185e --- /dev/null +++ b/packages/dev-utils/index.mts @@ -0,0 +1 @@ +export * from './lib/index.js'; diff --git a/packages/dev-utils/lib/index.ts b/packages/dev-utils/lib/index.ts new file mode 100644 index 0000000..13f4f23 --- /dev/null +++ b/packages/dev-utils/lib/index.ts @@ -0,0 +1,2 @@ +export * from './stream-file-to-zip.js'; +export * from './manifest-parser/index.js'; diff --git a/packages/dev-utils/lib/manifest-parser/impl.ts b/packages/dev-utils/lib/manifest-parser/impl.ts new file mode 100644 index 0000000..9138b1d --- /dev/null +++ b/packages/dev-utils/lib/manifest-parser/impl.ts @@ -0,0 +1,39 @@ +import type { IManifestParser } from './types.js'; +import type { ManifestType } from '@extension/shared'; + +const convertToFirefoxCompatibleManifest = (manifest: ManifestType) => { + const manifestCopy = { + ...manifest, + } as { [key: string]: unknown }; + + if (manifest.background?.service_worker) { + manifestCopy.background = { + scripts: [manifest.background.service_worker], + type: 'module', + }; + } + if (manifest.options_page) { + manifestCopy.options_ui = { + page: manifest.options_page, + browser_style: false, + }; + } + manifestCopy.content_security_policy = { + extension_pages: "script-src 'self'; object-src 'self'", + }; + manifestCopy.permissions = (manifestCopy.permissions as string[]).filter(value => value !== 'sidePanel'); + + delete manifestCopy.options_page; + delete manifestCopy.side_panel; + return manifestCopy as ManifestType; +}; + +export const ManifestParserImpl: IManifestParser = { + convertManifestToString: (manifest, isFirefox) => { + if (isFirefox) { + manifest = convertToFirefoxCompatibleManifest(manifest); + } + + return JSON.stringify(manifest, null, 2); + }, +}; diff --git a/packages/dev-utils/lib/manifest-parser/index.ts b/packages/dev-utils/lib/manifest-parser/index.ts new file mode 100644 index 0000000..f1f5818 --- /dev/null +++ b/packages/dev-utils/lib/manifest-parser/index.ts @@ -0,0 +1,4 @@ +import { ManifestParserImpl } from './impl.js'; + +export type * from './types.js'; +export const ManifestParser = ManifestParserImpl; diff --git a/packages/dev-utils/lib/manifest-parser/types.ts b/packages/dev-utils/lib/manifest-parser/types.ts new file mode 100644 index 0000000..27464bf --- /dev/null +++ b/packages/dev-utils/lib/manifest-parser/types.ts @@ -0,0 +1,5 @@ +import type { ManifestType } from '@extension/shared'; + +export interface IManifestParser { + convertManifestToString: (manifest: ManifestType, isFirefox: boolean) => string; +} diff --git a/packages/dev-utils/lib/stream-file-to-zip.ts b/packages/dev-utils/lib/stream-file-to-zip.ts new file mode 100644 index 0000000..80a6a36 --- /dev/null +++ b/packages/dev-utils/lib/stream-file-to-zip.ts @@ -0,0 +1,24 @@ +import { AsyncZipDeflate } from 'fflate'; +import { createReadStream } from 'node:fs'; +import type { Zip } from 'fflate'; + +export const streamFileToZip = ( + absPath: string, + relPath: string, + zip: Zip, + onAbort: () => void, + onError: (error: Error) => void, +): void => { + const data = new AsyncZipDeflate(relPath, { level: 1 }); + void zip.add(data); + + createReadStream(absPath) + .on('data', (chunk: string | Buffer) => + typeof chunk === 'string' ? data.push(Buffer.from(chunk), false) : data.push(chunk, false), + ) + .on('end', () => data.push(new Uint8Array(0), true)) + .on('error', error => { + onAbort(); + onError(error); + }); +}; diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json new file mode 100644 index 0000000..69fb5d2 --- /dev/null +++ b/packages/dev-utils/package.json @@ -0,0 +1,29 @@ +{ + "name": "@extension/dev-utils", + "version": "0.5.0", + "description": "chrome extension - dev utils", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit", + "postinstall": "pnpm ready" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*", + "@extension/shared": "workspace:*" + } +} diff --git a/packages/dev-utils/tsconfig.json b/packages/dev-utils/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/dev-utils/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/packages/env/README.md b/packages/env/README.md new file mode 100644 index 0000000..9b146a2 --- /dev/null +++ b/packages/env/README.md @@ -0,0 +1,31 @@ +# Environment Package + +This package contains code which creates env values. +To use the code in the package, you need to follow those steps: + +1. Add a new record to `.env` (NEED TO CONTAIN `CEB_` PREFIX), + + - If you want via cli: + - Add it as argument like: `pnpm set-global-env CLI_CEB_NEXT_VALUE=new_data ...` (NEED TO CONTAIN `CLI_CEB_` PREFIX) + + > [!IMPORTANT] + > `CLI_CEB_DEV` and `CLI_CEB_FIREFOX` are `false` by default \ + > All CLI values are overwriting in each call, that's mean you'll have access to values from current script run only. + + - If you want dynamic variables go to `lib/index.ts` and edit `dynamicEnvValues` object. + +2. Use it, for example: + ```ts + console.log(process.env['CEB_EXAMPLE']); + ``` + or + ```ts + console.log(process.env.CEB_EXAMPLE); + ``` + but with first solution, autofill should work for IDE: + ![img.png](use-env-example.png) +3. You are also able to import const like `IS_DEV` from `@extension/env` like: + ```ts + import { IS_DEV } from '@extension/env'; + ``` + For more look [ENV CONST](lib/const.ts) \ No newline at end of file diff --git a/packages/env/index.mts b/packages/env/index.mts new file mode 100644 index 0000000..3517e93 --- /dev/null +++ b/packages/env/index.mts @@ -0,0 +1,9 @@ +import { baseEnv, dynamicEnvValues } from './lib/index.js'; +import type { EnvType } from './lib/types.js'; + +export * from './lib/index.js'; + +export default { + ...baseEnv, + ...dynamicEnvValues, +} as EnvType; diff --git a/packages/env/lib/config.ts b/packages/env/lib/config.ts new file mode 100644 index 0000000..3e41c8a --- /dev/null +++ b/packages/env/lib/config.ts @@ -0,0 +1,10 @@ +import { config } from '@dotenvx/dotenvx'; + +export const baseEnv = + config({ + path: `${import.meta.dirname}/../../../../.env`, + }).parsed ?? {}; + +export const dynamicEnvValues = { + CEB_NODE_ENV: baseEnv.CEB_DEV === 'true' ? 'development' : 'production', +} as const; diff --git a/packages/env/lib/const.ts b/packages/env/lib/const.ts new file mode 100644 index 0000000..25650dc --- /dev/null +++ b/packages/env/lib/const.ts @@ -0,0 +1,4 @@ +export const IS_DEV = process.env['CLI_CEB_DEV'] === 'true'; +export const IS_PROD = !IS_DEV; +export const IS_FIREFOX = process.env['CLI_CEB_FIREFOX'] === 'true'; +export const IS_CI = process.env['CEB_CI'] === 'true'; diff --git a/packages/env/lib/index.ts b/packages/env/lib/index.ts new file mode 100644 index 0000000..6674220 --- /dev/null +++ b/packages/env/lib/index.ts @@ -0,0 +1,2 @@ +export * from './config.js'; +export * from './const.js'; diff --git a/packages/env/lib/types.ts b/packages/env/lib/types.ts new file mode 100644 index 0000000..919f80b --- /dev/null +++ b/packages/env/lib/types.ts @@ -0,0 +1,13 @@ +import type { dynamicEnvValues } from './index.js'; + +interface ICebEnv { + readonly CEB_EXAMPLE: string; + readonly CEB_DEV_LOCALE: string; +} + +interface ICebCliEnv { + readonly CLI_CEB_DEV: string; + readonly CLI_CEB_FIREFOX: string; +} + +export type EnvType = ICebEnv & ICebCliEnv & typeof dynamicEnvValues; diff --git a/packages/env/package.json b/packages/env/package.json new file mode 100644 index 0000000..42bbc9b --- /dev/null +++ b/packages/env/package.json @@ -0,0 +1,30 @@ +{ + "name": "@extension/env", + "version": "0.5.0", + "description": "chrome extension - environment variables", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@dotenvx/dotenvx": "^1.44.1" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*" + } +} diff --git a/packages/env/tsconfig.json b/packages/env/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/env/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/packages/env/use-env-example.png b/packages/env/use-env-example.png new file mode 100644 index 0000000000000000000000000000000000000000..f8e080f8d3b44944f383fdf670178dc54ecc0d02 GIT binary patch literal 42432 zcmYhiWmp?b6E55qin~)FxD!?1r1)@T}yFycP$#EP~0sz#ezE=+UGsr z`Tpd}#fIJ4+1Z_$`<@9?R+2$Sd57}q)hl#4SxMDbuiyYL-|LVNUOo|(hZA1C`u0jr zQcMkOaM-c$i#wNg;`tOY)!`DKPSJ?I`r#F3Pc4gf?U$iiny*)(79^&T=-|Fc(eTr znRndy*r*FP)LU23?%&RMWHvnYF1IWwQ>_`LkeRX*p&`PBnaG@@6bAQlsW7s*kc~f*CFrVH#AWn6W zWAmV|=efNx19bPC_&xt;RN&?`LGNLTUVvY}-yEO#MKq2!Tru!6(Xg}y{ugBC zbDWqkO~J3@z==QKZaX4ctE?2 z2M1C#oldVIPRgnN9-M!rJ>~8R3<2Rzii7w zgJdMq8O9IJjh4lwl!yxZhc733oUiGmDqz{yhD||>J4QtyYu$gglc~;)jM*WA7;&aE zzQ7)=U-o-ruod}G-~Ckw{%h^Ce1oir!=9u)E0M@K4?2g$#Y!+b8G-I{f=q|Vx0#N< z334!^usPyGP6yGEH)4qAC!^2B$nKMlyKPt(Cq0eoMI5tk*pU0R?(So1q6?m%6v#!& zx`-T7t8=%+F*~lzvPKdkqgp*>ReE)3u6 zX~ym5?v5~ZehTvkgJSg6LStDB`|Z9Df5G+ruugjGHBwVdrFim1x-^udi;@Nx3$zV- zlY?9-KXjU_%0K66%!*C+BL|iZyDSN+&}tFUNj+Z8FUAKXxlhc|DsR->sQ4dKSk&$pxpgil?e6WO0GE;vaLAO?}h zjgNi29xKG<91zlv-WrI!Q^di3xbw?*$$eK;1{Dlp3u7!Y5)h$>79RSVjz@~daxk1} zoX24miWJ#r7MIW!Q`UBL!kgN`N7>)!yaMd=*P+8542;)gHmqz(`gw18dL*(`0yj@w zU%LsyrhG)BG2Ks}4s36_23?-(A@7>*@EU|KyBXJyQA`dtZK@ROp+)Bv$U%@{l?`V7 z5wRjkVBZYoqCNeoHH28Z>&O8z5M$7V)@}Z@Y`Nsa87%wb@|@B({qc`6p3JI(LYEau z!k_z(Q~Xm#Wp5w#`0jrDMhpq@ozAYRpkbc$RBmQ%lp%_4Ec&^&Q!#$V0-P8;5Tz)Q z_6@@94h5W0-RQ}o2-r9gQjOcXT{Mk+HT~FzMN&t&%`(IM%du=#5z^2&ZCsi$cQScg zfxFkD^uTm{av*yS^epQ_Y5Bed4t`r%I*8Vt7R7*{UK7Mr zU68e?WYl-mYvRaEJraSR(f--DJ_q?6e(B2r=$n(#Z)49`Ha;gYtTIih^qzs_;&c8v z{+^{PI|->R#1ugS_r{8Oak9=d4KsR`kUil)8J1D)C(<%;`#ym^&6{?KF;{0jA^scE;xP@-!gLXLoPZg zo}v=3=OltS=)LSj27Xt_GXOoVC)8bQCpi1P_s&B=?34COZ`21VACo!OqGqqoy@cw^ zLkIpGT-rHT?krvI4M#b!@WyW5GB}fO#oxtXM&|Ku;92kU*_x_KbXgHuq}lMqVv9sq z(UEUlHg4>3q>(^Zyx|fsSxuuTHR#xYK7E(o6%*e{I#z>M!%hZIQMGE6xdDfHH-Sj; zOuBD!9nUAO1Zz6H-(6~-wOZo?rs4x(NcAK&6u+^4Z=^S?X9tpEYO*_ubYZz)8mMJh zNj8`5g_RQ($bnO9Pg!a@mY75d8@?Ncd;hpQy+5T~J^=OJz#oVG=j2sbfVJ|jXG*y= z*J54y2vJF2$2TeiC>_1R&MBIK$Q!@5!)ziR>D}q8N_hSNS*wqrF_(7!A1?w|40yW9 ztL#Gs(Xk#J{GrJHT?~2UqJ%hRqTYFi>Ct$OrFqOj6`HFA z0OcM*Wn0Kro1a9U? zgPH43qAr%C6GQJtw>$$xSy9xdEO<>*afwchq6DtCbh0`Ssz4CTZ0pzdWd&>Xk#a1L zgQ=1_AaQ7QVfv2~;$>EFA zCy?Y%0OPZ~IV1c<$}$rAV_?r*h$bWJ_Xlny%zbN(J|Jy!Vm&ISZZN4 z5UJ9j|H7i*oTWh_JU0ZpMr24XvUhuEaG1>B1KJ(BOuBqX zaHEaJOotE@eRRzrm`1{!Q;x{B9Ou(W*wnw_ClxZ7UXqP&*tBtPx4p#^09Xfx&^m;7 zS!@i-SWsLDILItMei$TgnAt7wWtgkH$Hoy>nPao-3x`RUV>CE1-C7>;v6!kVbd``< z=-4(GX+OUQh&;$)vt~)qyvY>+;22li9ML*HAT-M2^A8kM>{kekQjK=yp@BPS zNt4y-TDPd@I^(2^!O&Td$SL?k=#*%9@L(y=i{;Vffk~d>_3?74rK{Ys8zQ=%v<%H| z^@kUc4-I*OU5<_PfZm+>I$$DAK8GGt@puQ_xNJ0eZU}XEN8h&1jZ{x;y}eWZN`H>^ z_gQ^Ck^a+F^ogQFdCPwn+Ow;K*kWkDGzcYTXz1X@jpJNistOtr22EMv13Z@O^uBuq zVg|2dmx=I@`h*Tueac}K>-;%I0gN9#&HhDA)$A8+eGF zF(?H0C~i3f{YrcS#C164VtHN}z!&0%wl^#w{1v?9k_)f+_&`v;+mmWSa=w3wzqhwz zD`S(J54E%$yZCYQh1sTi{hdf;QIjl#rZ-vNR!a;F!*r>%8UASR@{Zl3CEdSiyhYJR z1iu&?pd((v`KJ{2ysR6vRFR8C@6CEQp!pJQ_LOS;z&TVJ-L|=qg!lhUgSEqB96Iuw zKL|gU6y9_j)TytlX%Z*iHZZKdX1_SF_3QqlRXVMBge}#La#_eb9jb7)Rg-%iZS zC3@6UNnP;kH9R-gm>lmd7RCnnUc0lh=F5T|-ayOGN;UAQMevp+A;mEMGBI#^5-O&5 zBbk`0v`dPYSeUa!5JWV-aj6vMO-;*{7$xc>a#^bJFO>j}6ypJE=*LVyW(;O!1+);G zYYiLr`Y>YMT~Xu785Jmv4u^hjEh@xVAm-kmf9-ojvTkzShNw&vxMRj`6zin6Nha=X zXGp_M-x!u9Vu9{j`Tgg7ns+e{?T9(+agX`fd*R4C&H6bpMd$Y`NskPP%>BbD2Qkc) zo)|w??3mNr;EB(Fu`h&h%=3rmloIs!Id-fD!ji9H6A(>@>O7NS8cpv)biS_rfh>$< z8Z`1@w4g#I*53pTw4m1G)x&}3Yb&yW1I1!y4qW6JnxRN)Ury`IcPWnd zgn&X6wXCF*&6?EXO^}8$c?>a1Ou6za^n5g5i49Lh!ec+ZhX53)*h!ZMu6>)3T+Kgs zKQI-|2o)Q00`$dMB87n$ChN5933oNOejW;q34|}ZpE?$M%S^N~Uu{U8H@i~cbDnu}=ZR*4A*-!$DdZ3D zG%J^X22?5Kx`vbe`OFFo%ozC{?#gONE-j3Wh%RIU@+?K_k(e3VHegE&g~6&=0$TjW z^`a;JD6H44#iwfIi6l2*pd1bGuhG$g2BwyqCESIH-z_*E-q^0T`Z1ya;_ed!CVUI? zzgcZJSm(vDUwBZ5labUIZ|f4B2*uQ7&ee2S!C6NVXuqlOvEv}kw4Gn{CXfk*^L4lc z*;MMIy15=A3)AL-Jui^m!}h>A*YGEJyl#C0l&Frn)wTN!R~MZUmDFRKi(KVru%(WMm<%RE~$9n&4SLLPLI(HCzobft{tokpwMnoLJZ>-d*_qw9N-ZET4o2V=o4 zN@=F!pbLb~&U5rt0LI9of_!jy_-BYNtZP*dA0PDw7%E*O(KsJDCw*{>4u9edT@#gS z_oMi-%5;n5QL*Pt(t^I%PVKB-Mn@`J?UCXlh4BOyCiJagBf*f%eQb|kX=gtKd;qyJ z&AO)V@WO53lvYGHC&gXdF_uR$yD#N!$W|Hj+p5>8N@(3MnBkVyZ z#}ZzrLtjW2rh`o6Sfy2aBu)l`F@0%v$)U437%F7R!{Z@!r)#1k-M284g%-1k-&_>- zL4;2ME?PdNuNx30U|66I2cHMey;1yPkts<@`8uj&qw+B|aOiKI46D8~>KPdNd|aKH zK?uaplLFEAnRq3V3$!#kq8*M~6t*%$O6pA0n77B3-t6EY{NwL&NGg}bd%@sZ4)?Hmy`cfg6_W?Y=w;U9G=FO#5&6IgMANj%_+O3sWY7!iMZ%gP>t6w}orowdB{_^{GoWAn?$HDgu4DO}kn7yF|{8qVa6H8N)* z_Uo`zT)ptD%|)g-m{#P}$@coyo~rYY?cF4;!>{zYroui=Wdu(&2ax{~l(;+G&&b#> zP!Zh4h~CYJXB=rvoBqvp8B6nhDH&@}hdfSPQbV8u{OrNVL}7Ir-ln!##yl1my~X=Q z355ylFC5`qS^7n8W9g*rsw$DdYdkIVar*uF-$ThwDe5^L#V{>)Lmw~3Qo-2`H`8A5 zw0d$qyUAbVj9Opz5v_#0=LI=C)yvj*_#c`^6EJ;(<@F*EQKa^09UcE(`Mdv+H5XZ5 znJiZDbAa@`@(X0fZq6};gt1TfZd2Q0>@N@o7L4@~8JQRp=Uj z%Usu(v0Tz!n+%9Q`p-VuiAu9O14_mvypU0MKO;Q_&CiYRo3KN>jeg1Ji%YNyCbM!R zutm}FXBa1@+#9(ytL!-ZgNeG0iv8~}FhWvpuqm4}qO@A)nQ_QZa|2!iMNAXqUg%t=P{d<%P-4}Ia;x@GUSC!e-{B*#1oBxuJ$ox>GXol!w z*dSk;5nIJhAR?Q{j)U*_7aPiSFT??yjx0>@G`*eZM#bF!zxo@v=!?Jrh=I3QMncNk zDXD;aHi-BDr&JhLo~}v%m+Aaf2+ z_+Ri(#3jQjnBSAvBNhZ1Nngh@>X8T>oBbwAsvX1CI4v0QcTw>m$kvt{f0HLxM@L|% zEs?M{++TF#uG`#fbR0UXp7qmvqmR5avy+siu_X9!Onw6L;hx0k=p|ZqBqi*6c%9%@ zwW&TQzQ9u=^=R{A9FZ2u1HIr6(X9!vM@|(PPRuNsBe9ew+|U2)O3> z6Sb0{loFAQdp&e|U1L4*>sUP^b=>5KH5ht|GGIwbuze$>~_yBM!b0X zpY9fiR#i$V$QD!98{aV3_2+W;PE*o9-rNpStX6NEeAJ}xToDaii@w^hF2cp=@b68I zi|HQ2Sl<4_9=Ud?7Z#?&g_bJ_JF)0q+N8d*FH=z2sO+_9Lx3#>csFp(#pAgnTo)&{l{5gzQYz?G)AMBz9U8vQRE#-x1lSc1bIo_Ebr$j zdmR-VK%!LHDlNp`#L4OqEWHFTaE0!w*0*GS?eihpH!Nya))3EbpkPP_l%t@kRBvY0 z7!|J`c07UF{LnA4+;LB4r}v1pDd95c{=U;dg+pbtue4_5=CJGpc6s?5bkzXBv!JU^ zLc+|bgg;;t?%8TknGdWf{5k-=k8!wG7E-L(1n(wz-s~LM!%l@D{T-N*iMtZS!QT5(i%i9ro*lnxBRs{4L)2zq;~4N$q>G zK`?fF@WjMygzb{6k&R7yz|*Ig2`5!g1PLw$CL>0x5A5@P)m7N|$@09L-Li@XL`i1l zfrn4^Q3<(|=2QfOp84YNqQQ5Z=JfB$LgW(4VPNex*my^5z(*2<)AJkyJss?yWivjJ zm#&*t7%}Thl_tzs0v}sb&Oi?L9uMtB*dK|pEd(GTx_y36qk<&Kp*C;n?;@g@PG{2C zU1x9DJI#5Xm_UoZQ@Y_n8;*v*daqb|Z`{4eDl1ToJ)_7WvzHr9eOF6LTsvX5Z=p3- zw3T*NJvoAGu8q9VgU7yZ(Q$uuY(!O=z+Y);^6m2b$8`Qy!gek)1~QhT$xXkVEm`KU z4!8Oif6$&%(J!ru_34mDrTRC@cM*2vOQWE*9wuPG5s+D1FiF!jP7J*VCmk?wE9{7(C5)<`TGNQR`e{@XCNK5 z$;L*I!VT;OfQH^4KDXLB6cp#}-ppc$K%D~{=QBDazy_Sy2Z}KQS$#t5PCCLgLwJq% zX82%F738q>GA>I42Eajn=ZSX&qFdKMU#C9N-cqOuoVDa-6mm>h9U;ir zhW>8W=4WyZ=IN~_ro$IQ+?_{(uD}(=AQ}E~|HpgG?7TcUx&&lxZEeyEUysPlJl@Fz zyrBvi1@If(6+@`bnz!D5r@yJXgu}Lcry|Q{Gxv?K9qoGR5^fz!R;Z9$-ZLw!o8dst z2ex6y_~&@mhCmKrGZ$rM_(1VdY2~Gvjje>S&lf#n4JRat0HNx3yM}TvdAB(vO^weI zRfnVn9R)FMokgA!7h2t%m~J)>Mna|HOu3MiF$8V0xr!D4=vg1Fh~C;--FI^iF3EtD z+yXLl|B2;qShpbb#1NI@X?tu)1tSgpVVR%YRluPbNKUvti3REHB37}De;fC!O+^i7 zt{Gg!Z1gd4{4Ydb*FPy6hA)*npi2lV=re#CsSm3{q^#N{*>y<1*pf4!4;>~lRAW(+ zT84D}syc*O5o0nQkc3cvtBtqDBT-xA$|SWqEVF_@O`#kpF)7RqHwqhvow_v?JEYh$ zQ){acS(sIQv4a!&dns>0A^cBk72MS4K{hrkvhcmD=H;n`_7I_oK07IF&Rh%~0v*r0 zp;L_VC?vwMS)GtBLC(nE;x+mD)=RamG1ED4-nm5+{;(VOdW@M|v)$W9X-9JEn?WY` zcS==qpJfx%99@WAy*>mk*v}f~XAIV&b-lA;c4t zg2DaW{=S$SuTLyH(za1rJYs<(TSAujO>7qTEEr8^N3kiIXin~^2uiC|CVQUkE1yZr zEF{5-2BK(LmgbCgJN$McH`qmjbyAnYk-C_jy-V?T8sv8f*C+atyKkT#VqmX`7`H(7 zT|y7mUw;U@v;xOhNt#^_xOH|$^}xzvH5R`CL{ZXaW@ZfHzg2!%^+P7#UDU63k9OZT zk9(p0@V~FFfTD|s2FYvQ#$Zo6`ag;zSFY?I=u_CbFPr2iPc4!zD-^{Vkz7EXHUo>RqqXPDmy%#-Tos8CUc$2z8M4RK%#X`F@;!MuJ}&jq4Kw=bH$?B2#~vBN zM`js;)AbT(H!4v2H@Wdhp^`lmMK}asi3$Qa&$RO}48IURx7N=i<~3)fv>I1fZrXTl zH!##`bRSz%y~&58)Z|~)K-D1~eMyc}G|ycUjg$fc&f1MB9W z+ezQ2!? zKoa+R(wgWZ`24|KV&u@0eBB)Qu&QyarEo5&q^vxVO#>VrvECeJU_EoX%-s%+`HBy) zUZNx}mLS+6<&4Wtf0I|hfy0)SZGTR!BHt?pauOpg+5 zf;&QJK~nvD(zY#Z`07wZlWT1l{&BVvof%bx;Z45aPys|Tj#qY05@$08dN1vo*U-p`~oJ!05LNiOD; zVy`+PI_`|7%!pwD+%R|{@o0RcwSY@~#Nh}fS* zd>SPv>b5_=jP(xGen-Yk>k^YFfd{Fms`_VU0?^RV)_?lJ5fKxM85kH`02>PCWJkip z;d`RGL-@C1N%`ytR)e{Sm_d-}Q8h4@d^>skCST2H{4%DWM#vA85NJ1i41Hm6RtXuEGCEBc^$wbt&`N6xTGZWhOWQ4Ay_X7H4uwvau@Kmmq%0 zgwF|Xp{yMB$52s{t7nt-9x=Hr;0g)9R9}KDIGevmD=PcWl;CqEE;5O6#jZHXzGwlj zew|lP#h~!$@b>mp&8SkUped@}7xlsJE@La4d?^i*6aHJP-bQqI_y?8iR;T;}!v=w< zrRohvVb!+c3k`d97rG~_3Lakwo?9W{UfSZPk!wm%;9d*9zK&YQ+&nmKV-cCBm21bV zP`XeIf~XbaBk8E(wC0aoBV%Inii<-%^>uYe!Z4x#*#Y)o75E2g z-J&MlqseEZ!X8trx7OBn>*mj%dvX_WqUb~X%iO=hb_~J7EXxg<;RxTOUZJO0M{l`a zINM8pJs13rjt?knm2PfD!>|d62t{SoI6K$VyL}=38jf_Y8B;=D&?;`^7!$-tP?aVb zL6H);7~StQd-@|8)18F=M@fAi((O^b&M@Xe!3|4|*0gn|s<1lrWW){I1T27on`!;B zBO$)&GzhJ)!u)ezSUtf)c~p0MI#XUxzOv8o#G<)XJ0E3!g>(3V#_N}(#+%f%t_b2| z&*&|31R^PFuIaIr&oUlE#lHs#29Uu7sH&*(b|J&tqNrx&g#9U|%y#RGwR-L08u@ID z!Zn>(b9_ECH0a*xB;S6FZQZ<6F;MLfkx1tGDLOSiKp(VtKQXVV0u>WMP0I9`FNvxE zh};=GZC8h8_>68UuUJH)cqx@ng1+wBmi#Pro?2Khk_01B3h5etU+vBve+XRdeEr~6 z4Htkvb8|e%-f5>rKzRRY0>GQkGW|#U<;pya%RijTvRySlfA zZ9PIFO_KFP%x8Lefff^WZhDz2(pDyl$ z)}O$V$Oi{RnmTs9miA42vH-~~0wGHgIk@tTq8x0*x=9^m(@Yt0sfVdi*%rTM#}x*U zikS2;zGwsY*(dEyQcYDv1$P?IcPBI|$dnY0ZE~Y$-ls<<++T^ZD%3AQ;Z?6(M$daP zk)%yZox@(Tz%<2602UfJ- z?^UKW{b8_~9%jbg50wRFx3pfeIgX^Icaiwh4kz18rw-SBOCCl}pLJh~FrJz6K7wH{ zATM{#J)0GgvR3+h269{1>y$f*iJ3C^WcMYQVKf?ailX7S_~&cE2jFTz$H<1JEe=jW z*AW8_?U{KFi*fD+-q3b#-(`Lv;`^-1SeNeiqI|gZi3$n7M1kD?Sy!>1g5#ldLHa*m z61f4N&k9t~Q~42XuaAL{zS1uox(7;nw2;bUG|8Z%;~THH{jh-{vY6B<rPLn{cdYX!3GWvz{Ku^O34z;OUwxT+tp3!k zRwf{KPTMz*Uz$kj&0+9~%R(Z7Q9V7XS7W5YCP@}7 z8&~N=o2?FJ;7}K`gvy~{w*FD!pO56slO)x;DLUNV;B0nWQlSK~p0PYkZ8y_jelEAh zca=--k4z-l-NQm@h9RmpH5U~E=1Vqpv~fq0hB1Ct;DsmB#aq=mul}f(kzStirk$h$ zszoDWmmZW2e|O29SF)g!?4e{st6J9^NGj^=OuI{synN*?x^XjRIn!JeWOgj0ZYUKf zqhJ;coB?AHEM7%Y3F)^3GglJr4D@1#&+;m*Oqh0sS3 z=r8ltb`blw8MEU?0$ktznileBnpwjXvB00J*bl}sM1VN1QK1R%=2jnCyUXC}yv2~3 z!O3dT%fvpmse5tOM!R?6%ta)9ye6J%T1sv~a_$29EIrf-Zg*>{%G zx+PfAf{Cn>uYDxVCY$F2kz9vi-8g*yqL=G(kruQ9J|ClA?l>dh#Y9rr!syBRkzR59 zR_KlmWn}-j=fktcVK{3MlR+kLO)5COC!p*k$R6}OrkOh5g!|~NTg}y)Zs$_p$pJmZ zSIX)PJx9FVcIG3ydo^db(kqiU635ZI?VKcDvG+&)V76kjr`+{$-tW(!KlQ;n8C6T6 zypRt-Nj1o*iKIq@2Q{9+$#Q5X41QJrnuV3mK2YKaGX%aHTBrb3HKqtw%27CaPnQ>i z8kUP8oVAwo0AE%BVsXnHBT{aDh|M8Gx?S2lEQakJL4!gj*8OJiww|)&!D=BwErJ?Y zdy$A8N_S6QWcA2@pQj}H_dpc+v@bmrj9B?!Xc$tSIZ~$SseoQz#O$`K`1cqsIH8nm zbC%l~ft=qHKJ+q#N?;>X<7p_w3j+43@NJVPJ*lHf=o$KM(d6@`z=J0rLBLQIT|=HR z=7Mi-iA)tKt7 zp-2>}-Q-$kbM?UrL5vyLr$`IT7XXBfGXm@D12kTetqi~vpDj44hf_pV6hPxVK8>)&A<>XXG;RWK1CXX?e|rXCm_2m z^(40~)&4q9(wOdB&thaZH37h_W`s$JHEV=&wE?L8@^~w0VgXEQD6_2vRszd(Z2O-E zkkOI~UDeBgo2-hloU5qx8188(6&P;XW^9<0ec?MA%8*sFNJ0_k&tk^rJz|pnADrOW z;FGrpG`c+u0uc%M05+0g)8FO32|{zKOY!wf22^5HRYFn8RdZp^T4Ay+*5c}ujKJJz zbNtAKzVD|~%K28AnMGJfw#iSPxaQJ@HYfE+m`R&r{-u9aL=E#6Uz}15A|bm+DciRy zt|}B!`oOKnH1t*^lee&)oX7hT59uW>>ha|ZAAqO|9w?rBWfHzDT|03;7;#JK7LXXZ9#+E`S%0#nU_!*3a(n3EO z#lSN|^`WvNw%j^VgS&BIv!yB2iFbxkUIESlbW1gbO1$dh5%TF09GjEdDb=vdsCrz| z^9!CzZb!`b>jp7ZC7ar;rUW^OtmrKh$rtYu8q3_+6CWolI5+(uIUEln0ehE5DlF}! zFi%WFxMv5G5<=Hcs9v-Nw=VA~O8!PRFPSUFW}t4(NE>M*6&uP~2fOY8^zw8B2nj@d zEQ{20W`~~s5=-d(DtHe&zd^b9{b?CcK1!tKJovKvfdE3X5}S<-uTiY+JJ>W27lZ?*Wc8AY%ma!L!EpC5)PW7PhE@_q>GTyL>Uuj7<084S97`78qTa8ag#3e3VsFSJ*K+>0zH(kSolIBV0f~W)l`+ zNHwcI%$C4NCW>IHJpC!|i=uEtjVZx|2!YN(03Off=w`oUv!g)Q1tTJ{ko;9#H8*3R z{}<#yd`-jFhpMzY>pj!7UrGo2wjbQBwzwFXaStlg4c;>T;Q!ezJBCC^MOUN!M?%0& zxQmmJO?nBM|AS{IxY_Kmnu}Tc4ZjqPlc@Ebd;m<={BOV+_cc;dYr->Z=TI%)aU_3e z`=W}|apE)*>7^tu4vhm!NiY1;5Wxs@yd~jw_xgW?$9>?|Y#uGpFYLI=q2ffOvivXp z-RmHig{D+fioczzo5;?hJ@aShCr#y*SMU^iUNK*IPZKxijI|QpCn;(RnS`eMSz9U1 zBClJ$Rso6(_3R(7Cg+@6^jcqMPiDRxw11M3f?N|Ub~WLf7Ey_9$aitmm=ZWV2F&;L z4vaR%_>WMv#Sc;ZO8vB##pO0q4CB#8UvQTTUT>>gJMK$cp~I#|d*0Y0fl0zlORICE zU~r%KjOzFtubkm3hdF};KfOpvYiG*A5*_ft?3`aGIgS2CN^kOtTW(Qj{BNq*#L#0c zIXRQww{@$$v&9$u`JE{Dtp1{=$;Q=>7jz&owX$7pW<} zQVWlk3fPQ}N$t(|#e3$8qryTApRteR_*QqFc4NRqxfszcibMmkg^kgs9EFQ;>K8cC zU1y4SGj92@c#jFahq?q*yv>z-AZ_f2X`UB?o8DfHE9qde<^OUs_P1Z(uo;K_U5Pw2` zUuJI>JgVTtnZFB<10=QpTJ#--8w7EBQWJ_3R-=+&(Mhuk{#Vb?zL%;5hW(}Xi4`PZ zDRNivaV8ZXKmB5-7SJ-x4;~D5eBDqcz>&!kYQ%@i)GODk(f>lPW{37@w6Js9b1gT% zLTPqr>l=J4qv#wOB*XfbPq*>=zohcMI4l3(buxATe*SGBh{M=2F}42R{wAn^;1>!! zM&O0`_4zK_<^1;-FNFf&Vv)@8Da=bgl#zz?F&@<=<|}7lRN-Pn2;0DyL!= zB95deh9CYb49Fzk$~BdT#@X(g3-;woFamFYn1AVgi^OS^|B7RYA|;8=qNb<_$o@83 z)uFtM{wFISpGpGxUscD5+P?~ke>DmvVvOvGvEdl4Uj^_1+gGijuyv`we0O5@)EAR6 zCT;M3e@m^?Z(P(iRwKtVI^LARHnRUuMLz$VGA5}Uf5tM+Qo1*p>eocU(!P(!ORv%2 zO7RyRi!*X2g6V=nSepnN$YhL0J|5P0>^-7w?mppXMECK2B5?`v{n;BLYJApxee>sz zCw+m>f0*{%%rz$9_DJ0_(^A~MrTvlfU4W%~8*Oy!s^0euoOA2PV(+roGY84g?e8ct z@|Y#2)TFML+k1{@D$54p>7*`miZd8ol=?lUKDtC7Eo6U4LORqeU%G!F%YtmyF`mYH zH=HjP@UNVhn;M^3URDZHM+wv;-yCmw#x2te%V@Mv0W+gs{|I7S$Zepcg}+P9lm7T8=5Zcxiew`sMncA~Q`mg%M%oJ8XPA+Vr4B z6bie2{vONY7o>7u40AwSCwby=&3am^Ik{Xju{<*08cP!LY;AcBo?Y%qo_l_aNdJVN zCj&51KR_`Hi_dmxHFs{mrMEd=^(*Xn{`N@kD;beT6ue_!eg8|;bhnbvIE~z}G0hP- zXG-04ZSJK7$Rou>Bv45+a%|3i{VJo4UvxvWUM^XH$7jP~E~k&%<@-w+x}7n{n?7mM zv>EPnwmMueDDHJ~6s20%YG^R7ar4r7b?gs!q#VndViA9s14}T7Jv1$$?+J|W)ghJ2 z_QzCT37Lz1j_J)pWiwb zRp=4DE$siZ5DQjR8X`0U~A zaycAbR#4#bR8RxB(`811tPg+C3R$A2F{=Nje;(d*>`B!G{oK{x>5086+rBxod z;)<{9(F(S2HLlAyk2_ilBwUp~*m}=2o+!Sro~4rXS%+iAR75d_DDU~Dq*tK~A>RkE z%cZ{ws3A(OWdwJOBE8kYhOvhHSXRgpU$ExT`WlKm86Li|Kf<2+fJ9Tw)^D0**sB~v zxfZCZNwRWywk`B6YL=gXrgRnUNhu((cdA~v)?%?(hScaeXG=y3uslH=XOW^d-3|(-u*&lHhBBiS&D-159B%Ul!baC->P9dd}7~8 z4l%KZKr3M4njCBQg7xYx;n3ktEgF)&H{wah^=k~*d6)@GP>9YnQVhSR>h9UpXC%xB zIG6l(qY);RngUN_Y{k4Np_yBFfb8esDXLG`$hcVVAsNO3oM`NE#zegfm5u6x% z}`Lhu%{7?s46irarn5?w7KA^@t|8?(IbW(#%gcS zti-%t1M1Qbk)9&BwM`ueLbIe{u|Eu4^@Scm;>VYI9n&!md;lLJ`MQn7mKPY$Q?TV%Zsx^3-UF|ed&?W_+{gw>^)&v zAmZuZWU)0F9m0lUm#vcKJ)M~d=;e>zjvqp!jgS+4wyOr3(_HT=K)>dm~97IROu!Kg~9ljKzy=loH>(y?vHZ_X53r`ha7ppv19m-yT_D5fe8Ug2GO{w_N8 zevEQ;RA_S_)v~4Iz+wiHim!49EER)g(=WHM(*$jn{kneTi@N61`LK;O{a;JbW(7Wx zm93m_Mzh&HhLJwA{6VYvO33T|Df$uNatDW5kJMDLE;CGXcaCfIx;OD8)seZs}UvzBXsty>UTt5`>O?cS1w znFG_-o%>Vb7D(wJWh%^nO#OEC!M~8*$6@C9;iwlcG)cSjG=tG(CVxS#U+Q6 zItob{%ng?_lbu5*^%}U4oD6<5sahv_yu?|ljqM}|q03!K0I|b-t}zR@#**6XPyJn4 zhZSd7v^hz7@_rRzU`UZ#M#9jzl3FzmhjzBO;=4#zuqrEXItp>j;H$SqoJ59_`DY=y z{(iMzj!?=^tiP>ql1ZP*-bvj}T&GrM$FA7qe(aQUK|s#jBYOD1@Htlcn4&CV?j;OqgzyCDTB21^lv5oqEGb{N`*n6m;c zpI&lpoIWWzIb;tHkI*UMZ*YhhMrLO4?(Xgr=l`7qMHW&ZNdP8jknA~sO<6FsW0ao5 zGDbj#3m<@|yDsebpkTwK@S=G+uySeXLGvbUqt;42|3JYPj4@tG_|O~v40j^ZMI=){ zL5L=DLv*|7&j?aacd2ENpy!8}lyJ#7NN-cX$YxX>OHC5Uh1IDqMa;^^de>Y?v0vkO z@dLY0u#Vs*lXGP)3-cH0H%W(n(zFSm9%ifTwPiU5l3?Wi=|AaHQ&=$)@8 zQR#4ge=jOMW~9d`w>B6ppt zLSq`e;PpfG={e=CZOJ-rpyGSQn8xoNoMOesei2{mvZt_d*TfCAOfS)8{`{~kJe4!z zZc%~SQCxoA6@e^+-(UZvK|!6JR7&^mgsY@jOOc{AR=d-|MydaKQsO^qvvEgfUFMFa zDBAh<40Xh)N()2O+jq^e*_Jm|{yn6Ae1O{jA?&TA+G_f?(Yq}zT7o;F zxVuAu;_h19N^zGUr7aK~f>VmSTPg1DZp8`i1d2oH3BBLv`QG!bb=LVOYmu<`?9A-! z`ORhH&VUNI^U^_wp1J+OXATfy@Q{B8X_4y!b9G57H<}|f{|zesCpI**U4g1Q6_8qv z$4731Jr;!OdlCHb|Ihz&=wwhztczX=Z<_>ZSTTVxZXreG_s{w7*|X?7p@;4W9(m?= zJL5vwT=lM2un)3#NI@yVDmoq*OjEvhq*J8})tAi(4xhXmvNFOuVO1AadO1p(ofGt^ z`b)w(G-)O{r><>rkzcS(W#b?0FL``CPCl?~QKUZ8A00c{V{W)0ObS9~W8=w=-P8m{ zi3Y0Zf_X`@knn$;4mTVE@uR^@G(jQrng9yc`JKS&X^cBOemOFrOQYX+J6y831u2p~ z$zkVrTiz1IGQO$#r>htR|LlIg8^#&Gk#U$s0jSGmjuHg$Q-Sk5)5?R-uv)FY-H}A* zGgNEGc~S}NV;BkhjSU>4=&0YrGhZIu^d)PmX^`GV${jKvL^Ry>UH8U!si|S|r{kZ` zcfZpayp&AIC3v<70D5Wtv~?&fg1@;o-Y@==JEAyL9-Qf0Nut-ABlC+$jlV5dBK9Jp za2eU4m{5hP3gk%gYA8q+kF_fh5O#;Ey%08>i?D3j4oCSy33n|V`>00Ls}E+U-fAKu;paHX~iX9rZtG6gCu{XhftxuM5SBfx_Wk#WZ)O4S7IHv z--7cj_7Soag|WtUBHh`U+@Jb5A%iS`T$~&X!k&%6cG8J#Wh()?vqSc^S#z8lqUXic8C3Oi-?B4r1moj3YwN9ci3ayIt~fmhen zf0i>R4u|8?aVMxi#_oP^k$T$AKE_igO_ZfVmlvUv?Z-R{+z^XJ1cdL}yJu_|BrP}` za~t=NhL_F;=wv-zU%8g|{gEu1vUN`Q3nnxj8T>48W5__G02kPF3vq5=h}_9d}7tgjUkF`yO93O!|yTejXG4I z3r1XEv*&R*TGYdX-_cS%yN@V&mt@m7{B5g#xn)h8{SuwD(GK#KRzLh9JpVT{aRV?N z*Reu<4|&FC!um2aVgW+?3KDObZOL!l(EyaXz4nYS<7q6`{Og>e^2)g@u^a_{5uU`sqC85iYhT2pgnx>~C*nWpjMB$MYTRv6 zvF{jcgb5d@xPjvk_}BJlA3yvFdJ)q8)}j>Q6l4fD!45-x-6eS)K&tKgh`)LI@Ioe7 zHM_urY_z+*Z% z=-?Zs&;5+vff?>mUwWtbq2%^Mw%xC_dZK4ZVu&3!8wcQ8Z!WCt*P{9Yw;2r6bJR+2 zrQukxX#pKnyNIeZM^1u>zOl{qFjRn};L5h1%C+o%T~^j=z*Llf!=7uC4O(qZscyMJ zeKbO*Q*D78ugc;>iP->|$0C>)}| zWDFy7q}?@ijwB^95R^5DG_s_BQ)Ws#;>^QaPoIWA5uh(Cn%qdelJ!fiZW}kc5lQuF6;2o|hOEBB z+#_~;OUVkf44k^A<@~TLmfM|_245c< z!RTz>v9tfU`Kh}z>{I97$nu3t)KdByIlcA zW`Y(v;9yTEf8%pEFmvKt7D52RnnDJ!X;c_PH{qj*lghN7rbp;B!@TfWP|0ev;=}nA zw_7KjvvK%e_kZ(&Mi$C|5sv`0kZU>&E^OYliJ)}A92xA0?12xU z0d?$vE0(954nSc=y9M5J_rWxFr(p->4ez6MNwV-E&9D@vI%}84nUPrwLd;}sb!)l4 z@>>KXoEeYK+(*z*#deEKYnLPK_4)9OcY{WURQ1&k2MW!#OQ*Pmul?O^jL$f24Ga{? zy3o85k_hzKZ^Mmj4^x%yZF{$}R0R$P08Zxp;v*Mb)RQGTj#^q1+V)cKrMrUjR6`Oc za7yBIV3Y#MJ=n|~i!iEa^&<0w!rfviMAh+U^}Kx6(D7}K-p7g~1TBehn}#E3N#~-M zH=>lsHu;M3(-Rc8Xdk(YPvZ+ZWV)x#xy{UHeoP}G1I}4vPiYm)oQej%3m8n%fshbz zEr?*lQ>e1OW07Ku55H{RgkUA@?A?btG=PEvKNibMAm9})!p<%En_1W+ry5)XJ9$_jJ z!f&Q*bsoNc%P#vs+JCzNveqy8yU%tS4a#_Z7K0MB!4a_4%IrXJf-GWdKoZ)f&HzMzXO9rxbjr*Ecl}3 z+6FEpg6lI^_qBw9c||3av^|0|CyZSqId$%XW=tgyTdlu`xTX9|;9(P*v&kpljW=w? zJNCB@tbPPx!)Il5jF=#~-(R+UpR!&n#L4AFYOe=+4t_`_ls(5EXRuw|N2sZPK$jmq zgIZ>*@RPF!ETvs+@R?di5%8)NaucK%MG3`BEviTuC%Aby)Z1-pJ8xVjBMqLVCNBPg z+nND?bTg~;o%&z+kUxDdi|AZrZ$B&bZlxDO(ByWjV(;{b^2AKas!`rOOF`&lgsaQX zME>O^Go(tprWtwj>~>wBnH2;E8!Fc}*Ao;`t3qCk7RKP1d_bIl#PQli=oi=V*mn9# z?7hL@=*>jFYoaIH>7E{>?eX&aAtZeyzxjLVzrWVN$xe|4m6fsW-s<$>#`lt~&RTF7 z2_;n6v6ffgF(o9}r?%tpB6ozKI$y=`d#^~nar#488fAV(J61IW!|2?k>l7{FkP_FO#U)gN0hT{l5JviW<&ZI zBwvfUyV@fEYVQU?h#|(xN!;=ej?-(h$@k_W3_4YXs$({KB={1YuG^wJhf4uN15=e@ z2a4$q^uIweI=veZQ(-2So_*H!Uz%NP^-O04_>O?rKI3goKV6%yY;PLw2CkW573z~J z`a!Z>7H>yy3O;IBqsIBduIXV{48<5(GZ}4a%zs&uXnJN4LoWeya=UnZfB*l1cU6P3 zJ0j)|roZZaM4!Jq*;E*p70{V}0i|hqU!q;k(BD!`e~u8EIASVoOlQ~$tTenH9-S83 zR}OjuLfVVJ)n2h{*wk1m)-lS_ilb27=S@JMfy>?p4~pZoFFdyLPg&pKBmIjZZk#Jx z6KSccQtbAiD?Ws0H~*Mkz;2>PUfGJ7OC1A;dN~!=i2sPf*)Ex|Ytp&wBiA%ovs}_; zhAiWbVj?jahLbumIvn6DTk9;o9o557e8<~jOwuD`Zrh z!CATm;~Wn-ipO)k3BnvF3aajJr{$Oq6?;i^ldKud8C&9)W!jt)$GL%G7rxNpvn-g) z?cWv!mB4vulK$vdWr^b%J)=oK0OL3P{D~KU$bpQLh-^x=DLW$Wz#UXPJYO}Kjf|X` zHc-Zu=-k0CO{o50^2l*of^D$r<(TZW-e7Ge6By?xN;nHh(^DWgYj zLg2}eamd@O^l@DMz9+|bT6d6aNmf|G7qr2QHyTG_@hq80Dun4L(6xGr$f{?Q)FKmb zP6QK762wvad1y9`Xq-T+iZFTFFShu@kFRj>xwCHr5*4N8B$gU7T73F(3<@+2^x}!} ztdUoGn*Mg4c%vtJlA2P(g%h7^k~1HU)$`W`&Hc}Rc(JJnEkh-Uk~>SpT5TFGknis= z2n+H0mp02IM8Jb+#e1etPG(-3r(yX|&ST_$3|hoKf>g9I7kp}z8~wkh4AIh4`rAy| z6AAX0BHWi`Vk!97v7Mug(0u%RVUo-mW}M{u2u}3@`gbJ$d3t?nS&seblc&FGaQ=HS zfZ`&xKp~iVJR*T$l(Ij`(j1o7$c2dU%YL2ChK}xb9(z0Go=MO9e~8aPN_>1j)n6-J@!#f8bXqXC?13o zAjp4oTj6>Ffbwh>jBD0D)y&=lN6XUYx(^MysL7<*_296$V-ZP9S+_Z6cG;c%F$IvxF| zj$`(=9$)(F>~c#~W4T!YpAoR-~FnoRF2> zvtE9LMezsrJI&?xiY(L@lBw!OS-M!HAoe-(eqED~QRImT$M(NmcQz`b`y>$#ry1<2 z_vZJhjW>m1kSY}zN3nDvclLGqDW~@-bJ}X{%ipBY>kr9dI3XuUB|EexZ7Lt6?g#IC z2Ge_x`>)L#KB3n|8ug7LL*bVTqC^^MUaxF+4{#e)gL<+LdXD9vT-nzGVnU&-#zP#h z>dE?h**|q`tsH`9dEH`fX*kN7BL|T`Ox$pNI)%LeoQ?7W&K_f;e9MQ|g03l+JK4N8 z>QXR`W;vG|ugNjBo(2Sj{+C7l{+C4sl|PLPFcK~IdyKNi_3wbQsc6Ln-TQ}m(HbGPDv zOP2%r52*^$z3y_-A^I@!NjA`Tg7pYQI%)Q&j3Yy?J$FScQ84NC_-jh~#IX-6@*8;m zs$?4@4Vyy0Y$Jg}UyKW-W-PUHd@r7}eLBPPI`t1B)fMZxQ6qPi8f+IbLdoR^!UD?= zt1dT;mfOxa>t04yl?y}19oPB`KX`T5a9B}zG?bF>{k(owv3f^P-+n`en`>t}s@0&2 z?-9ye-xsEBk$kqGy9PTv%-lS(L(JR^y2<-J;YLoI-G}jc%ON-CL&yR@O(JOJlS-MF zBNe4)!nzCd(yRyLl9{oALPn@_CO$!fcDYs3y=L5)YoCIeLl(_!MIzmFPdCSog&SN? z1bl|ww$0w@FDbn@v0Ruly%^#3^FQ=;B%Ad7`f~};|6E;IK<4arYd$S>cE9DP_isAS z({CMt+;)PL`A&in$k!^_q8fxNf7Vu0{rNpEeKY<@wC}ZD%U_s2hncBWeL=5dc^Aj4 zn8flf$=D3fd=%B+&BFSJR!3&~soeF;XEyUnc^|>$Td2ZZ1XK znR41MKz^|pihWiO?!}#fK#P{rC zT_33ZN0Nn-1mngQv?X-6x__pep6xyI-)wc15|J)^(Oqi}Z-)B!ZMTlkTF&KLS*BUT`u6IL~9?>_(Qq<&w4+XVbF_-MTB+xa*CdFd6K8}kehq`ItES} z_}9M2+PUu?bQAEkr0A3NTCW8-!GMF;eFiHVe!BwIw>4t0$(2-rIxj*Qxwgs{?y$1@l`0f6rvtxe*tJ^gdS?=woXh#^sq%N7;T*q@cQgy3$HPaycq z7xgYuf!kx%p{~Ht;tZGr5y@hB_6tJi)5q(ZjSihhVY6R5tkxXt38+v2^i#0v8u(@E zt2*=hB}7cnBELXQ;Y;+)oDGYEF6z07AOv@kRbNl2Q;P4?ni1$$skXgj0^b4;8q z9NUfE&JXIHjcl^!r|LP=cFn?=L&>pOjKpyr_+?qtZ~CK4dqug=1ec7sPh0$6Z}V#! zD-nw}9Bd1#+G;mmSme>o%aM4kF`4}%SNk!jUqgh=?_IbF06%xM4rNX|!j!5^pQe5^ zSfw5*Y0S%c=L-6evl-&$T$OKVm5jchW<_OsbaM{cpJF}rWhI0Z(F`po6^87(Ncjozn6@Iz4yrY1X!5>?x zBZ?kHvtHoR;qdQV;ja_5(}_fUQd5<0EIa5J5EwGh$QQjfK=~g6-d4qfGxX?6gOnlu z5Ja2?VN;V=DEJl_@=deGTxdl?Psq5p)HirN%89W&bvjy7D$-BLI$-RaYi!LMx%Rz* z%fX4S_1BtzvPHbk;5|hf71~1NE*qx^od;gATIq{w`)nP6^JAjV2>nJtu)fHp7ID*R zxvhm8-q^&YNk)UjVjj9FhMJAUm8R}jYb%oST# zIQ>;b0Qv`~Ytwz#F>=-^d2(A$uwM(jOyU~~UD~*|@U5=A7OIH9FsZxljNJ^Rg$-2N zoT}7?U@@WL)afs>P=e-uc=b;6~ljr??vbNJvHt ze@%+qSUt6{FdUWMqnEQykW@cJq1Cdw)ErByI;YUQ=fg&%WQ;_WM+OVc*;{uYb8e7y z#%=lHIkr%8cDA;XM;?S3)TxL1p(>&#Oluw+nGPfK6R*pouQ~-N?Qd)Apdt`6$+83z1xXSvqUDM!u zO;y))+uQfco_#%{xqZDfFg<+CfbIth^!w;;=&>$QeW*j`F-W{(t-JKBw-XiS;^y?? zkH`a^i=V3bF%A=`j7U(KY{%k-9s>>{^3*iG!9js9gKFLhM(cHOJ(EP4cna;|BnW9H zmSu}Q)UB@7_;yN@QK=yC8x(v=0ej3v2J|%>xfKJw}xVgeG zvv0EMc%pbRk=W{!-B6ZjFewof{k({A`(BeijFLuwtz&z;9BtslrgwcYf?EnB&0qyp zad|%n#(Sx(8|vH;wPM@|7a+JCI*;u_hc^GzrF93j?wuA;eR$qIw+u8ymCbl$ZcPre zcSynlKE9Q6Z{^b%H-gUb(^b+2$>y;&uTN_jJ8AQNQ2a0)H1F%fZ|XPf`L&$!OmaWU zq+Za_&nGieaqTTq1kb4a|v zA6q)Ra+5t=#hCx$>S*7MF|boBN=-C_1;HN(<9{~ ztH;leu>w}R#*(uE7h@(u2nh1mPnzoAIH|)ubxldRIUrYD;5iq|yL-mQM^?|58uPPB zl$vdRzN;PRm@aJ^A!7SCs+4P%w_0dWS!i2-$db^+PdRJ7K<7k2DZwy5K(73356dL; zRO&}FfVE;~L|JrEshHipNx{9{H)JN_VmD9>*0)6?`UlQnOnxk$Z#ow;R$>3}H6amY zGw}s3xFakd%hT|YM7YGBNbr)It!16?w=t7L8zjWf<@Wn3APg=LsJv5*3TW~V4A!9S zflTlBbaeb9BP)A$cUWn$ROcWyl0}`YK6@3daUhsL$pE4th8;gGRNN>%tNi7I+FS&?v#af4osw04nHM%7RG5#vx2{k$u zIW&o6G+GeVxPwqsLC$hdPP}JGk?s zrtTfOit{ij{H~?vDAKp*c($W~SPs8+zBZ}vPVOAi+*St5_F`zP-s^RLO;b^td-J@L zPooWZavEUwrv$4kfWsh=b|lQhM(-Pz891wRPtt>YjgR0125+qqVlJn`vHjdUHFT*w zrQ?=rMZu05X~hrfVUzgO?#YXgrLt7$Xe8-0Fhz1z$LhG_8G^M4Hk0l08x~pH3-PZN z`NO8KQH5So@-}VowRm3=IzBtj75*^cG~|HoD+d zKwY`G-PWtWU$dQ}1TL){Y-Tz)v|{^K{D}n2RbXwWj);}Q*8#6*n4VB36PP$5blQUq z)eLs&^F)p7u@Id2y18-Fi{e}tvno@M(u)_#EPPS!cSi?kj><}*fI6PFKZrRpYT^T5 zn1zP%gk0;+-ZjLMh@Pip0XwP;Xqjjqy=`hH3GojOh!d>xz zJLZ0>L9!~@+Biq!CbTMRANH_MzUCQ3mZbAH@SoQMJn801tm#%Nbn2cReL7^|(=A-B zD}B8%s0=PbKUFsf)@pO5*Oz|-zcuc1_P+9*iE|1-5DJS+N=7Dmx%R)_Eu3mQwnoY( zUu`B{`}g&e4ma3cfS{TN#$C+-;Cik8w%><%IQZ8n|JkzJcvw(~n)SrX9jDey0s>b3 zo{xq9PABazVQ1=+xDQt(oH`*lV*V=N<^E2RNB@q=$mqR$0p4@0O#a$!vhhskEgO`C zQCLDp2pMB+`6x~owc+rt9liJMpwnd3vppd|b9ed=WfW*|fzCVPyolD?1b#x8_$O#I zh0iC8HM&s3Y}MDYpM}ske`;T88$&``fgU}4455>Sl|~u>6z7KkE>~h-QEbDSKj+E?ZH%m z^-*#Kqp-?xPJeass-W-9JmxcVQip+t2_ciMDJMt?DMAzxyssoL#ivO{d-}1=Oi6|C zm2V_UHgHNez%H+hKo-g0M6G&sCP<$aBI|&`CoZBK{mRj+B}Vg|bE$)-_GUhp0+JS> z%Tah}*gIBT#~9TsW~@y~8B8M;q6UW7kT;@1Z+-l{Yw`YEXgG3XOAz27pTTmQf0`r< zExe;xgrZ-6>dCLsbvcDO97Qp-bB+a%J)*HOrftH&XJ{_KQ_~RkZLD*2gl$QKP2O-= z8vLL%^u~B%*-x%5>zxC!#_bTfH|-Y={-NFb1-j-pYrR$1tq~R!N78}LUz&scaQEaI z+bKOjQ*40Lp`=P2gh%pkJrbe6+67@BH@8cSlWPnke0f||6cU=|G`4}L?tUMQe}WvI zevFIoSj7U9gG1YsCgakiY5UM7QAZ9Ld>czA;E;Jhqi4CSD0PEz+?}wb5nXfAw5{ zC^Z6}HGw7SW6~r!rEH0$@$?B>xN(79tW5;!Ukk$|P0zLCS#WrfM)%jdbYDdh;q?kQBiYP0>;8E8ro zDc!Wt@K=@lcLreFi^QrQd{*oRQJ~C|2O~Y0V>|5B3r2&}zVrAAc{r39kHjY~gjHL?_+1v8oD( zFOnO6f^#mutcx(R+celOsGDy6bZ|UV+h*0-5Qz@WCxx%hi>;5{0^nU7?TIHlqunT# z3ZtL#YRPtj`kAx45F$Y}<|g0Si3u!1t6IBxHXc5{?`q5v65upLk;n|wOd{1NhTzo9 zGw|nY(#>nhZXz3@XwB|5nBP3g0~3Y222Qk};vtD6c~s0>wU!DQxjH^Y6llQ?9~*m< z;6K0Fj7lZtQ2hDD6v` z{)EEHUETDJjv*`R%6$#zf7Bc6il@NEZ;lNqXy;kXzo^HC@B$@Zuu&*nFO>Wg7j`D^ za_$#U3@@uj7KW;93>}}kT<_4V`W7Si^?ZX)iTg>VwHlmWnn`1}=ljkD|D4Occ^Gzb z-enUJI0Z$wFbtSjmjcW-SI2Jy_f9@R4VJ~-%fpdTY;l2rD*iqSeXo?6vsBWHi6-&w zz(=hF%EZd$SXFu~cg2$G$5~aEXkp7!>w`6IQC?IVnFG^_4?jzbHn{- zF|C+VH-Y!5Y$mjbqIt|%Gqv~-UB7TU+X1}UqiuNq4fy7Ux~UDm7N; zM>;kky$2BM`gVh<*90eRPzIp1)KU0%Hn37Oz7Ybc4F&q1W+MU&-9L2i!8%#S;-nO=D-A&7FOrAdU1y9c)tniSP z#8h=tZYS06!a(#;raLuFoFqE&Rteg2?eeXOF=}u>sG_;F$_M5QSwEZ0ZSvtgXy66% z4yMq?{>BTE<#Tn=%s-P@dWYDF#OOBt>}wo?(=kEOTrihPRfNM`86PiWjDH~zYF-@7 z>oyd-!hNf&%a`mRhN%fAL39C)oIpY#9;|q~5e=~K*@OzC#3MzWu`f(@)MfUmBE=_m zAJ&3h?h^oQylPz?I}g+Qt9p~yjLI<*ms|q2T$&i~y&3+eT;oQ>s{E+f`5=-fk)_rp zd03$%D%Sqnk*X&=;}+q|V{+KzJrsQ?%ZHM0XhuK%p);kcFLhrqeS_u7;=c$S%xdq0Cv>6;&{3GfNe=n&!;FC>>? z!3AiKX|D@Y#cI={8EsY@J2E$8Z?X6&-$7*ixOr%ve-Xg+*s8Z%_8cRYTWm5%d6}D2 zzdS>koehd*g1#|ng6p-#=qeJ&*@RN}R?-e29`aZ3$cmn$0nkj6bsES$mL*PYn?Y+5 z*|lpx*_FLpal=nR5;k7(L%tNHWbqdX9v?jDbNt10`Qk-_tyvqL3RJ4l+rFvH_!th> zjnLD{icrCHz_gz#TLj`x-^gUABCEl$NAOnpUIc}7Zpm^?1>QMRFZ0ol5Fp!EyqXDG z;zk3ctcT|Q$eo>m$ex+IODDb2X+(*5xfI4V#oe5?LZ7-YOUSJXRx8TNsv+h{2(Q@8 zYDOqKKKMkf4$}$YTvQxxlF9k#^NEZ^u|1Buy9#Pj=eyucRIx+>T{n&yz57X|r4OZ8+5Ul8XK+9_m|~ z)*e6XP^{VA$STtfe|a+zeW#}OW14t$9ycf3I34Al8s4y3cTw*C*=s7U&Lt$s(MX|w z;lk=?la?Z}hE18Xi$PC?I7i!Y(GUL|%_`ibfVm!xdBau9Recq<`3-y8X}t~Mx3hJn z`ni%HkQ!L!KV~orH#A-tP#*T>Q}QJ)rvUdqn4hoqGxgJW)cwA2V)2(jWiRu=s=}3R z2r$XSDa9ShRS8=p#2t}8kgk_ISGR3PLWY)@6j zged>gp`6jpFZl*;1=}2FZ|)F%`IoZdPuu5!4M&t<;?)b}%XhDvzz^%4fn*oltum+b z$J643H&gXMN|cpr4kDv*E4+X+G5KE3*f4dk9ZvL%^n;r@2Rd3;9e=Z|wY()qaqy zD`jKH+4zrA{QLqwkEGzF{~+aKqm~m@#b3Udsf?i15Aqr~EH_0*eN2^}XVf8WZ*^>| zo8xtJ67$7=t;GzS%6I=q&y~|f>ZFB8iSMUK6bd3BMlG}nZo9Fk-C3641;MQ-v7*A+ z_ogxmovXt!$QIB9pH)K?#vQ-U+h)Vg=`cV4$83Nw5c-ZZdhwLYnTKF6BEMtQ}RwjVn$sv}5O zGYSTy3v7!2;?bVV68b^s@!2#l9VH)yjG*x&l`=#m0PFWs7}V?}OA02$gfOBP@+os0 zgU~kfM=T+|2QvA+uc)!Hl5Q$=PW(`yQ(RBFfrZWye4r{CkCdFhmDF7gT>)+rV&y|8 zh$|I7o4wd+x%dg`C%ScHIJlV3ssAsJ8?*jsYmHvm)e;3<8xYT_Ugu6C;NyOQEDn5uyYqEDkl zO;%Pxuux({>bBBQ`;Pid-G8}U^45ko6N$)wZG@br!kUAvDh$un0(Q5 zCLR4(lp77fa=kJ*U-n{90lL&rYKD*TLDJ+W@wQ$X7HU0_4sL zOz?)lEkw#ze4A%Tu_%cVCCj5>onKj8Cpuv3E%~VILnp>!R1gepZb*;u^Z;U zE@m|7zyI)b>WnAN-(R(ax?Wxv6)9I|tTebR^3=8$JsRn$Rqjy3IWJ3GVDaT&A;r@f zvg9Dre3k)!g&1{+Z8`sHGa|dF@d*}UQ2(~IfY_qpzisJRO41=JIOrD1U;jC=oT${3 zuH&zygX`aeqSJz8vH$kie{J>`4Z~_$ofd7cO7P{iJH~`o9kn&jvJw54|Y~uu~(%=;{fQmBHvk z`#O?XmR^8b&o68?U*zDRmnA*=83 zss-{rqh?HDualn6KxDzc^FaI}H<3Ok5o^)KkF1Ic!s!e2oqh59JoC#6%4kdW zgTPsDHwB^tPOTs{$))xogR1Ipe3kuC=4hW`&}IaPu5a>hAmbe62;Y zpG^+URNd4D@2~VZw1OsQ-7)CzoHmEx9}SP%KX)s?K+z8U^NSyRwYmHg(2yS74I4^u z*!#M-IvU$7uqO6}-=YRTdjw0iKPQ=UIE|G0+c|jA0PSci4u&)A!xGP2ZPFTyIC4FHTOZ)4KwIo5>C0O@51-O2SwEZUGE@KX#~?rEIKctK zTAYdHJ0&jJU!72T3M;56>u0?ah0+#ftustQ8o{*Y%J^MWtPBX&t%w|A+u6fmb0DeN{76w&y0dsBJ*Jus}t^2(NzD3EBcF~Y#&+U5J9vc1#KK;1?5 z_47SI!_+;fAkz)2%`ZN$T3Q!r{Cf1zPk12yf68@{2lw!{rbRJ zXB-yLZn3$ciJ!MV`;%#CGeNl%)&gCStnPJ_A5Bq@O$1|cvhGT8~%GLlnPx?7#C--8!FV+8z*J%>&g@zI7o)vQMkB}(_M ziE`Q>FfQPdS&tqWu5vSwpFiI5<{#^uyC?GB9;? zfJP{2BXDtwXUY$!VcB?0-GMsiEjWD)j6gXnP27#j;aC#n_TQ50ncg%P;s@HXk702- zJ9^NwxX{N2znJAWqU?h4dHQa5ZR*W156hylgHFs%5+~HJa@teF(@-%C+{SX}iP)N)xbvu}w!^EChBt_BJFi52k}XkIZ)cBpGHpS!!?VYpf3gRLzA_ zkWyM;X~nY646IIAKD-rnE3haaS>(jTC{czit=ty)#5?SEWszpjybo29jX& zP@$6)mY-DWv6ODfJ&lW^6hk5%u9Ff|MrHDUDr$6>C}9TBt5CcOgy= zj<;z))mt9#4xa)AhK7dX{sSB{^a%IOW>@VsO&^PY_`nVlt=9YM2s5L34%_*e3Aa*v zC|Fx3>822lHXliK-6>!KF`Vq#xc+|S>Er6_%!mq5khDPeTG>}gRE~f-sPOkS*P|5$-c}-%h$Fg0abEVwm*`-u*-TDYB6rH5MV`g z%{Ubk0tfkb^Bf&NF`LxuYw%|_IKIk#(Wc*oNne7sO7NCpD0$bVjB;Ym2~tezF=pSU z68FaztTpmxyA^>Z%a0jLd%sTNp#PQ+Gn2qE^Z7B_!fVq-&Ro*SSNiK-V?%h7Fj~vo z;_SR2^*9ale^C)%N!hh2^)iaiXW~YjrDR(-O!J zh~chp2ErP+SrD5wx?urfYRtAaE6cU;5TUng8*{M_5RXm5=DKeBl=NIP5D(P0S+9)n z`SEX^h9=@N^|5DB;HmK&I)`5?h-aoHmvYiMi4vQ@3NNCv^B0nw57htk;7b;g_3@@% z_TVP`SFtl+{~wk7ZTt&JQOic#S2hfMZIr2Z*>cPe86*`Hpf)Qh8?m4Od9%{m^ti6T znO3?Pq)d9Ha|fxQ`*J#gpue6);F@|n7$YHZ35JSIiGTdIsD!{#m&l*0N}_Z2>A~Fo z8jTrLv^Ss)Zn@n_1)ntTN_9yx@a#SRRln+k9znpm(e^(CoqM#4kX0`HrYIZL-#3b= zRPqSPrE60Za}Iv}BHGGbYOTLGHsq$In05COd4G{V9&T-I3mJvsnIYE4)b7V44D*C7 z)6&P*cY2aFBYcEp&+gWvIxD`Pc;f@Fd}gqhvfk{Z2TN(+Erw|>q18_iub7+t%!l3^34qaq{{Ec^gv{zmcF3XC@VpvA9h5I`Lm~0=8GF7 zE~1>c)4H18*?ITRrjjrl3L}L&{1CtF+AfY!Y{xHgTsHW_kBg+#J;@@nWrP(N>HFM4tqGE#zUEvirt zY+QMqn{Od$L+mIs(a-YFj41G(Xw+n+KJY9CRxL%JyBb?2B*wR)c@74JC^^ghEATC| z^hAY<_+HUKf+wwe9)I7*U#GxhPOYRHXPcET=uDbQ@jr0;5N4LGqCmS|D6{_UPBjUe z>`z?K8=*932a#)|rW0yiM2_49d5F1(16(JZ+^r^JyqkR>a9kHC`rdVtznMIzeoKES zMk(H@kf*yshjUPnF{}wSoi!X{5dZy^@not2vfqr$YgvuMOeMsP1^M;Zk872P=$`M6 zR^taZUlZ1Fwm@-(sCyiS-rf~S zT{%}{PWg$D zl862)U!EK>gk|H|N_$n3kdSO30zr=#A$@%^1h&$M2{OJkWD@rXce5C~Hx1#61Hk<) z9{jC|sFnL&Y;T0qPD#F)YzL|+G+m9-fEMT6UF;l#z%M;fafJ0~tppZ@eHvaKPNi44 zKEbOW1r*cI^W4}5sg2qSEal`qmz1_v7?qD~rSE27@UZGdJcJ2tyWDxY&XG`X7f+O? zn6**mb6ahGGp#ndr!zCn?Owg#+^8w$`3FmsIc^5TY^0*epWl@Dd+(orXnp0S#I-mX z6$1k937xo0Lw|XjD4~xpq=rfpu;l6C%A4IkSHXZLxt8|4wzK10jg4C|LyK z?(Txfj-sZfUdGg^){7KikKPhdnCsoPA=`8xL2Md*NCF7we&DQ#Tm~%uV*68y@$laN3NbacD&K#Mx(;fds z7;)*Ol!Zr;VCs$$v34uVbf}D9#3i@?#;ze!e+>TzK;&;{wOe!p(NKy`vw|=xK7J)q z{1n~K)>&77iv`SpqZ(3rlR<|h(`OJHF<3Kbos&Vsm&eZJuqw(orzYZe_uDa(h5F@R zKL`R30KCIYAg+vTN^F!ZaYI@E=01mAV4b{yE=IPg)w z1Hoi&e1+Sgeiv?R`A86w_qi*uYd#;}5&3mZwy@~nFIGJL`(u}xsEq#ZOIZdQ-|5Wn zgKkd`Rt>GhdE;@xnNAEdD|v&aZ4P{G?DHk;ZDOi6_8Z7V%8;V!J1-qCs)K8{_uLB> zp>uKfRtI>^>!tfr<^73A_kQNCGt@Ya5Z&`X=!n}|I71!bu~WhVzLyaThy~7skizN& z&FTb9-FRbmWrd&Cx=w+JwFZNh+Qb3|iGuXJ%fw0@m5yD_MEk$cKjA0zPc7^T{c|Jwf1`ghU(nzx zs#AQAp9>9=ryQ7DiR;)9y{v_ptCk6>?XV)9HW|w^_wbH-_)>YRXLytOQ|goAho^|CIW(8>3SGPw_88k+dMp--{9pJVG4a3nA7W*Or@dVsnrVXL7{K1BY>&eC z(w8Jds(Y#ktE5!p#Gcyya_4M1%#s#=jEMAbj-0x$7!yqeDwh2GqK8 zJ-Gu0w7pfUyXbBiRqes(qD*W^a;EXv5H=f41sGM zBWWQLc6GyGvE}m3L6T)b6KsSS_-5|B!2Ynur3S8te+ZG;)}5#Sk%)`QQfk&$#|*iB znVeQ`satB03w8EhM~kjp2%Akj_f%7q0MlgENR!_4xy0T5Hv zw0iws&a@2Pp-5IOv_Zr$JAP!$TH0R3inN>k1Y=SOEwiNmiW?NDz*gzNb*G zZoczdx?~eWFbV*q#pF~+3~+jeuy$n>43TJs%;jPJKV5zIBimp5euq|3ja8#&t=g0t zMWc4@J!;Q}lGwz4*WPN^UM*_xO^98Jpwvu5?Y)`^;hVmn&-487&0p}!xzD-JIoG)c zSzIG!A=QUI))uYLBeQ=6d78GbCA%MoDeLNgs;o6$E^GX0#V!Xgax>y>f`{@8fP~&r zH7G-+x%C$YK!$I0B>QaI3odJ=oRdXlKN~ZaGZtk&B5kSlHEoL&3!J8P1-|sM?PnZe z@%r>HGRUrVVkm(N;7p!4(XAU?=lB_8Onk`hlX3$~Jrp5Ulhc2|%`1NDB)xUPs^kS{ z+xKp#;*nm`_!k1h!%s}&Y{iRqF6i({Z6U|3ZMfB5fA{y3c}j${%0xQ16p0AYlVVG2 zlBZ-lue;z05^?jfy;~b=Gi~OHd3!-$K&-AV9pDpxk)(jMIa(R=S9{GP2OMwE-g!FE z*rK)wIN zyk}MM?;wHy!w8|DpBm18j3&ibq$e!$pUmB`afZA`v8=Z9|GGHhcX8ra=iz>(`>3an zncY#(#l=zN{8^Ed6Vmsz5CQXD^YAe*InX056Z-E<*#By{ijV%q6m8h>K`SwU!XwP` zp*tCqA?pi)iA%tKzQ?9A(=-hi) z)|G_8-3)9t!WcXl!snPaxf-U~v}c0Yrk1l*IuS7az)I??)ui#c49vFNU@tVfpcwO5 zZ7opTysQdfU;Tw}^oiJ)vcq1A*t@42BjD2iAH)=6iaavbJtJfY+;B9@N4H=cS8fJz z^m(Hb-an&gFHSOH10O)1pN3v)opf@I$_3IgGxu$6mE~x}3)#7)ZZ0gSU?CD4&%%_s z<>-fmW|;?KpeD;^cb$J&8>{`^{^H;!S8C#oPaux9a-tuCcF=f;{;rzg)RP6%Z>NXX z$<7Gae(oB4&e=YDZ!Y9(hKgz-HbCdbd?x3TL=F_Ro{8awZ{E;}Qp&(z{tN|et~Ut$ zoxFc>R;cv90HuvQ{<1T!0X>_@jV)|btfhJt8|sliNU_S&r4csVF9MUl*QNxv$ghxZ zb;!f(2y;$>3&eT}kLvK9&eP-ESjgxoQ1)^A;8)wsz&Tca%`N@W>NK-(!VJSH{8y^c zM^DjZ3rPb1$y&eQR^}xBv6*x92}3ZTqSL`}I=ZV}e?V0}e)Wt1&>eS==N)$De;UA{ z(0?IKF?P7TN&tI!WF#>lNC$sTq{35Wy0+*(MGs3T5Gb$u`6BKF9FOTiGt3rm;vO~m zQZT)kaX2W_P0eDcO{K4ml9f#X z_vY6u%c|eqsCvzP@%^z{|D#$+FVECVLq1D(uG4yo>r;0UzBKRD@=V8 zyNI^rJYh$rkvk(UDhmo%{;@)qmXA_?-0nNEFDHmk7o+%sSD?n^nSWTnPguz*;>f&? zEI`>}(+sCHH9vh*t$R&YZ7>YXz?+#U9`uz-q7z*kjqT7Id~Q}cKm1Y&Y>jJEn>(am z4cSOhU(6P=&%}ju4B&4QrTG?7ps|YMocTC>r1(cNIxU&5ho_8H#%ArT*Ej%laOO@| z-9xPuu`E+VK%fEb>GkW~3qEpuqVATt@s`t&O#_iuip-y|qa)1Kf6(@zyRBUYFHt9$ zR5yRrU#jV6*LLe?>TmIRK=CaEa5h=WHnhyoyL5i3 zk!~$igDYPv{B`FTzk!f~xQ$)F!@j;^Gvld}YLirc^;8W3X-|@|2VP(}EvfaBr~AUH zi=bQav-T>RhFJpV{z0E?aI_rzFJH*@6%0-ycfx`4ftahAmLx87YJ7X-?{zn#$0}if zOshM>Q3>y39l$XVYmS{+Qnz)+g%|y3u;tx{$IrEgffM);n|1@^?#+oZ-(Y5H&cz@O z>ZahB^66m3@0r1`kIpPHwUr;d$0(yXav?9Nn{xj_&jc%Moxzkvap)Pe>sig9$ddX1 zMd}{*83EcE&X9C;RD3oZ{QAcR7K0w(S~)%>hgv_J0Jnvf18xYA(fPx$jM^zx0+UM> zC=f$^fIjFuy=MHW+^(%~jrofz;g{xGOQ^nfnIfkI!QZLRMU=>+H98X(={VkC7Gi1UA&)>r=&rIxCfsL%c+OYoS8q2^)@NS#b=qCYeZalMG7I;kA2@n=B(yX_B$sVc zhwiJiTg-eibaTPQQm4Y_<*MU9x^vJhsZ9^S5+4=vIuEeglxEB41j)Gl9EwCZqejyn z@q^h_GZdj?Y-sT$Z$}UMw)U-5jA4lqt0Xqt+Ps}WQ8F14P3lFjjIac0 z3bYIA=Y#NBL%xg|Be)c<=*Xt;q=8XA^Z6*VxsZ!f71+3GxS|Gg;m%l%tLM*@?U?e! zr$Wb+3{Kh4^9A%RfA_5BO+U!F@Tpb0)?|^s|2RBSi$_{>Df|ns0yg_+l>@xoh`ATt z8z|9yZoSfZfiX|IO=6PRdvRcdks`@H{=Y#F`_G`i_-kr`H;Tr$#Vx+9(S5sfO*81^ zVN2cxeI*Ba3%&2LX-C7Ncc&l$U+hAFye1f-QZhH>E{-H`P@mW(fn1R$`_@G6}z{XzH? zt=q5YP?&m~MlzDnrYLvFv)x^Ra0tPynPyJ(@8HVfJxF+eSxQA{kftRI)pOJ~KeX)} zWu~#@I69BB<-2}mZS*2ITEhGTRyzA=@?IjF=Ydmd`c%E7HujGt*+K)|DZ7`6@E=CG ze5UXqP542+b{8n6znVd9cCWX*K(FHndMSE^+P-*Gd5j4>W5WR3=%E$cfR`sCyU|0+aN#*D~F?J>)aZpe!Hw@VEjsrpmyB;!pOGu zvSqwpE9niEi-$j?Bit%0&?C)D=PeJd0`wkM zwAKs0t_$WL0TBc;k{&JcElHAXXGP1M`--8SmmYY`b=}v<_HOe)JYljDiMi}cR&(IHTo|O z+)tg@!>9jIA=Lq)kV+PoePoY=le7>9<(m^g# zw+^}g`|s5m+ggVefMG)-c#EJxKOI}Q9@f4v4!6w!h~vZ1{*eQxv*Lhx;;oFgDDY0- zVE2bTxI9Q<`Ikh5zfEoC7}xl8=A-wc-91x-`?FNMiBi4T_h$)lHsFF6LDfo~W1)Aq zcAY|NI^#SwxgS=)gE-Ci`o$JmO>TeFo)%9iJdpgUs#XU*R!v>lnG_LomZb&UAz81515mdv!8BXZ7aCdi5Xa#w{25}3Qtea{uYel+@i=BDa6c4t> z;;kq^H3gmCjH>VFY~QYlrGotcf6zC#ML`FB%_WjAPy2P+m<=dlMLS<;YG-!(8%j9& zn7dJ* zP$PT1BW@LZO1#AU7fkruFKu^*@tgo*qQtXRc>(D>-%k8npTD-8pHRUHh5TVj9j1%? ztO--wST9;}RKUlTSOl8p%4qJ(yykDE{=4eEPI(b;7=CH+kN*}>Z;Tr)7q{}X<1bGq z9yJSJ_r7Mp%U2NJx8fxWGsfc{?1=faF@poyMv#o#E|H^DgY9p*q+3JD)Z{+{ujU!d z=;Iz1G|VPZxuvpp-Sv9qc{^|^V#dqXl-8cYO(zOi3TF$r&QF=ixh66=Gz;4g@8aT4 ziHt@x=WVW#ULs`JJpa@vG+x~+OoWASufBocvKu}2L-ujOk-z$b6~6adl3i^H$DObI z{_sId#ynzRSF_LOgqi$m6$C|M$VPeKJ9E$gOnA{znM3(s;>zCviU?^P?&qUoGC}3& z;Z(P1_^4rT4L?A}ZTO5^sXXp!R#Ne6I&^;|`p`BoDQMoCYsA`Q5K@uRO*NVxqa`Uw z6%JwB;hQk8y)&Seks|gHxe1Oc)&$2GS)n@5b|v1a=1-rDzuoBxvF8;|FqQ1ylm-no z@Qx0nK}p7IPk(P0QBdKm>-HokLfAXEqDTM-WN?B`Jab-el=4}I+Rdr;zkA;CqMU$$ z@Kb6g_v5EDj6^8ezKVC@M_exw`-sw5DVd1HM54DHisghwcKZbRjPPy#f0tkgzWQRq zL;(9^6uMa$m1HHEqm4Io$jE$~KhGw8=0~EcI#$2?@LZZ3Tqfw<``&H1T7-7~6WA8i zoF;B`rWR=Yb|={!Bt8Fk7Bx=zv4;`j*)NI9L|;&%qm`l$^wVQjuR%Q12&Hhlce9`k z5&$kn(iv(?F$a<9+3%29o6TIKEo(kTpNZ-FBo>s6v8xR&qi@Jl^1B`uTU-Q9L4Hu@ zV5SpWy>LvBrPjn60kfW7lqlC|GK8~G92O@ia7_*j7nW%jyTS&(c(8YdebAf_i{9@Z zb8ITAkqI%SL3?;TPS*mz&8MnA&m9))!+eUSD({5Xv2~S(HR4m@X(?yTU(AZ0wV0fZ zTFQ^o--MWC=y@);ZNrMkYj8eieuUrTYkFGc@|qjB$!8C)#o;&6@h(?@%1z)6{X&*W zflvNGdxlJ&!Of;{NdkVO3HeLnBC>i%0_a)^A7=x&x&<@ys11j<{3JzVDO^m`woJT4#st4@OT? z<2^7pihL|gylNRdn@_)K;p34=ZbCl3?5eVWk;c~`KfKb&RN!iSzBVE&AW)yA0v#Us z*E1)M4WBQ?E=kNT?cCvdFi5VT8U}bxvl9s@h?EX6wdjgAO}vg!lvm*_@(&)7X>`+1D=QeU|uLgvZDr9-j$ZRLn= z^c~Kcm0lxpX6uvO=RdbyN>$X5L0CeqU+{AmH{TP^gszj@QKr{^Q?pn754L4<-# zuxOR3h`EW}*RYIyFY==HSCkD5JYj^$h(qhSA@QRh`-iU9H(ZhySv?mck*z+Ltb?JQ z9w}e*a~G?Bom9`-SleU%SVsV9{`68bth&6p)V#{x9#SsxD~AM!Kj*&2J@nFhll`@`D1bxc(V)iW z^R|Y!=a}U0AGuQ%9R$I)kht8-TOA20FT{iTyG8&A^O2UXy3Emw3iF(L%i-&3X&mx0 zszV=n<5MPx5@jsHVK}3+Kk>!vkPI>=zplp>a+XDbo54zlPB%1uN(nu02vqC*?<@;4 zDAJL$VUvRtysznQ$KIPgfY~hX-Z0M|UOhfs71L}vurE)KiL~1ys}4LC zR{z})xNC3Lh2>AEYN70)?)2NI5X)SBbew2Z``tb?=qu&&Auh^e5x&}Jeu16Ak0Hl@2T+f(cdVioXiiVDtOSh~LB)E2NGb%3EzCUg=;x77mh ztH1G30hVbUU{_hX@W+TYr3g8pFCN~Q`Hgp1O_rHHQebuGfckt*P11tu8*9B0_7Zc< zV{wEKt1G;SC&aur@>l>;tfvj)ORl({md>FkqdKT92}M08Qgh?IBX~`3^W*k`pDr|& zS?=_1aNXu z6|C4l5*f@7SnkKtAP0CC#O7vNR@zBw|8m_aKq9-vo{IY9N-Se6pNqDY5C zDfh*zi%CdNgb}hAZ!fd9*L8`plfiY;R8i|aDQ%08$Y`lu2E)s*iI$|6GCWt#)9{Vs z5BZGyMe|ysIA^VO8hy4*M_5uq%kv9OnV4n&=P&C>X2U354-E7Nr)!>k>eSc|kSUhw5ZpO$!dvzwY7dtpg?oWecqF)Ds4mNpo^ZfhkI5hKiMa?=} z6a8b=r==x*7#1y;VrjTtEPCZvTj>ex3&hX>06UVCL}hU^1o#F`Z?AUc7_MA$oYa=b zvuJ6pt~&a$8A9wRl4kU&v@uZ;F8>{qoIn#^AGMcu4J2?(_YRg@aAyHCs|&E2j=8D(GT8D1Co+K*^E=H1wq0#r}_FABWjll05~J0jF!$cPh5^K9@3qffVg zvr6l(%Bw&>_bW4nMS=kWX8!a~b<8>#n*wVJL&p`kUf#P?s&F+~tprAs`T6^xLQTcp ztnT7E$ql}6+Lz7?#UyrLP%-{)Q6royt>(+6(t^ z6nv!F9h+CDk`oMMVHeIa1g5X#Q{^y9r zw^=olb?Qa+6VtfCZx7*Y)0km>t*U)3n9S@kpIcd#ZHC_8ntcZemtzOk+IqzX2Pt&ZQtV1TgVe`22m+p{U$e7 z6IcL4o%szwtkaYA25vjynELF}ovIFp?Bh2Mg+tvxlHf0b+2?Pf0eE@O-?s{NUi{+P zS~m7SyFI>t5Ro)Uu?wzk4*rMUCpvU=KS2RN~3EjO^fPkXrq z><(y4@}NFnnR7iJeEz~6<{06HQCsqL(VP0p<@hSWY3?w|y*Xsi+=sC(lwKTC;$@i1 zz|dDh(t1gKn6s0CXc-R??4rZ}Wm+~J|&tp%uLDs6dlM8vRQT-=VY$^V*TMQQq< zQY7;(Q~;SE$do&9i&W5rXU+&Zp8-=Sf^OFT^r;1eiLK z>+QCb9bZo4;|2Tz`Im}sNCOF13k}l&lemS)#;dz+>6ee#Xrn@^DH|YHHCqn*VZQsY z0uK#;=^37X%E33{MUab6>enStP{*^df|B~=4kKMJx?M})xOb;t2p(~9{#&UrU3YTE zM7$Q~tinXd^S2~qxjojY<%Y@Unaq#__&v-AbOZXtt~qrXDm&k2h|TR#Vp=&5?R)S5 zIi`m?UK4B$JU2D#@B_?6*(Agl0g6|+y$;kKnWz=mQahOOb|(^kLzC*7Dm ziiB=IGq>jgmN0nB(=R`PQ8>`S+HS>XrVQrQ*#S!0O6tnTZLW7N6U^ZtOS8W7nsHk- z)4O}w#63YH9;VM;g7mlr4rcQL5;Mfs3G*I(KCZ{__ZBx@KRAbN6O{~w- zD*3lRcFBB6!iN^@SrzsTIB`Y~@20$YhdoYe%6qbq9~7LkmPx#w-X(tg3*po1ef+|^ zGl+vjT0gEveNFKzQ)*l=GZ8AOFj3dusCh)+XZtYvTXG@um+dKh4VRh>q!68gb*)Pu zP9j_;n|6&VeXp4jxK+~DZ{NAR^A!&hD1|0T@1_HagU~XpRvY4u#s+B7cAp#GC%YGP z!rbL}&rWf2I_y30V$i^XIg-r^1PYKI8ODnYX1@Co1XSR`^54vULaX(w6nxxS7oe0e z>37{nZwdt6;I%QqcR3X;ZP;vCPVb5_Z^h;crO!`SXMEy0t6KjiEG}-#(v|TDCoKL= zKY~RX-?_L+drUZ2;CfpRswRzxhBO3lm4eNEuXih51?|`VK{(Kan>lWudcu;v9ty`N zn(chO$M2<9GNNtqbYRrENK_=8$ue`n*e;_aapq@P75?NNexOWj9sqMz+z8)c_17Kp zG`)H!&m|XQgdsK&92|Qsz&G void }) => { + const ws = new WebSocket(LOCAL_RELOAD_SOCKET_URL); + + ws.onopen = () => { + ws.addEventListener('message', event => { + const message = MessageInterpreter.receive(String(event.data)); + + if (message.type === DO_UPDATE && message.id === id) { + onUpdate(); + ws.send(MessageInterpreter.send({ type: DONE_UPDATE })); + } + }); + }; +}; diff --git a/packages/hmr/lib/initializers/init-reload-server.ts b/packages/hmr/lib/initializers/init-reload-server.ts new file mode 100644 index 0000000..4df256a --- /dev/null +++ b/packages/hmr/lib/initializers/init-reload-server.ts @@ -0,0 +1,53 @@ +import { + BUILD_COMPLETE, + DO_UPDATE, + DONE_UPDATE, + LOCAL_RELOAD_SOCKET_PORT, + LOCAL_RELOAD_SOCKET_URL, +} from '../consts.js'; +import MessageInterpreter from '../interpreter/index.js'; +import { WebSocketServer } from 'ws'; +import type { WebSocket } from 'ws'; + +const clientsThatNeedToUpdate: Set = new Set(); + +(() => { + const wss = new WebSocketServer({ port: LOCAL_RELOAD_SOCKET_PORT }); + + wss.on('listening', () => { + console.log(`[HMR] Server listening at ${LOCAL_RELOAD_SOCKET_URL}`); + }); + + wss.on('connection', ws => { + clientsThatNeedToUpdate.add(ws); + + ws.addEventListener('close', () => { + clientsThatNeedToUpdate.delete(ws); + }); + + ws.addEventListener('message', event => { + if (typeof event.data !== 'string') return; + + const message = MessageInterpreter.receive(event.data); + + if (message.type === DONE_UPDATE) { + ws.close(); + } + + if (message.type === BUILD_COMPLETE) { + clientsThatNeedToUpdate.forEach((ws: WebSocket) => + ws.send(MessageInterpreter.send({ type: DO_UPDATE, id: message.id })), + ); + } + }); + }); + + wss.on('error', (error: Error & { code: string }) => { + if (error.code === 'EADDRINUSE') { + console.info(`[HMR] Server already running at ${LOCAL_RELOAD_SOCKET_URL}, skipping reload server initialization`); + } else { + console.error(`[HMR] Failed to start server at ${LOCAL_RELOAD_SOCKET_URL}`); + throw error; + } + }); +})(); diff --git a/packages/hmr/lib/injections/refresh.ts b/packages/hmr/lib/injections/refresh.ts new file mode 100644 index 0000000..2096ec3 --- /dev/null +++ b/packages/hmr/lib/injections/refresh.ts @@ -0,0 +1,33 @@ +import initClient from '../initializers/init-client.js'; + +(() => { + let pendingReload = false; + + initClient({ + // @ts-expect-error That's because of the dynamic code loading + id: __HMR_ID, + onUpdate: () => { + // disable reload when tab is hidden + if (document.hidden) { + pendingReload = true; + return; + } + reload(); + }, + }); + + // reload + const reload = (): void => { + pendingReload = false; + window.location.reload(); + }; + + // reload when tab is visible + const reloadWhenTabIsVisible = (): void => { + if (!document.hidden && pendingReload) { + reload(); + } + }; + + document.addEventListener('visibilitychange', reloadWhenTabIsVisible); +})(); diff --git a/packages/hmr/lib/injections/reload.ts b/packages/hmr/lib/injections/reload.ts new file mode 100644 index 0000000..d342ce1 --- /dev/null +++ b/packages/hmr/lib/injections/reload.ts @@ -0,0 +1,13 @@ +import initClient from '../initializers/init-client.js'; + +(() => { + const reload = () => { + chrome.runtime.reload(); + }; + + initClient({ + // @ts-expect-error That's because of the dynamic code loading + id: __HMR_ID, + onUpdate: reload, + }); +})(); diff --git a/packages/hmr/lib/interpreter/index.ts b/packages/hmr/lib/interpreter/index.ts new file mode 100644 index 0000000..9273357 --- /dev/null +++ b/packages/hmr/lib/interpreter/index.ts @@ -0,0 +1,6 @@ +import type { WebSocketMessageType } from '../types.js'; + +export default { + send: (message: WebSocketMessageType): string => JSON.stringify(message), + receive: (serializedMessage: string): WebSocketMessageType => JSON.parse(serializedMessage), +}; diff --git a/packages/hmr/lib/plugins/index.ts b/packages/hmr/lib/plugins/index.ts new file mode 100644 index 0000000..4d24e8d --- /dev/null +++ b/packages/hmr/lib/plugins/index.ts @@ -0,0 +1,3 @@ +export * from './watch-rebuild-plugin.js'; +export * from './watch-public-plugin.js'; +export * from './make-entry-point-plugin.js'; diff --git a/packages/hmr/lib/plugins/make-entry-point-plugin.ts b/packages/hmr/lib/plugins/make-entry-point-plugin.ts new file mode 100644 index 0000000..a57dccb --- /dev/null +++ b/packages/hmr/lib/plugins/make-entry-point-plugin.ts @@ -0,0 +1,73 @@ +import { IS_FIREFOX } from '@extension/env'; +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import { basename, resolve, sep } from 'node:path'; +import type { PluginOption } from 'vite'; + +/** + * Extract content directory from output directory for Firefox + * @param outputDir + */ +const extractContentDir = (outputDir: string) => { + const parts = outputDir.split(sep); + const distIndex = parts.indexOf('dist'); + + if (distIndex !== -1 && distIndex < parts.length - 1) { + return parts.slice(distIndex + 1); + } + + throw new Error('Output directory does not contain "dist"'); +}; + +const safeWriteFileSync = (path: string, data: string) => { + const folder = path.split(sep).slice(0, -1).join(sep); + + if (!existsSync(folder)) { + mkdirSync(folder, { recursive: true }); + } + writeFileSync(path, data); +}; + +/** + * Make an entry point file for content script cache busting + */ +export const makeEntryPointPlugin = (): PluginOption => ({ + name: 'make-entry-point-plugin', + generateBundle(options, bundle) { + const outputDir = options.dir; + + if (!outputDir) { + throw new Error('Output directory not found'); + } + + for (const module of Object.values(bundle)) { + const fileName = module.fileName; + const newFileName = fileName.replace('.js', '_dev.js'); + + switch (module.type) { + case 'asset': + if (fileName.endsWith('.map')) { + const originalFileName = fileName.replace('.map', ''); + const replacedSource = String(module.source).replaceAll(originalFileName, newFileName); + + module.source = ''; + safeWriteFileSync(resolve(outputDir, newFileName), replacedSource); + break; + } + break; + + case 'chunk': { + safeWriteFileSync(resolve(outputDir, newFileName), module.code); + const newFileNameBase = basename(newFileName); + + if (IS_FIREFOX) { + const contentDirectory = extractContentDir(outputDir); + module.code = `import(browser.runtime.getURL("${contentDirectory}/${newFileNameBase}"));`; + } else { + module.code = `import('./${newFileNameBase}');`; + } + break; + } + } + } + }, +}); diff --git a/packages/hmr/lib/plugins/watch-public-plugin.ts b/packages/hmr/lib/plugins/watch-public-plugin.ts new file mode 100644 index 0000000..7a33685 --- /dev/null +++ b/packages/hmr/lib/plugins/watch-public-plugin.ts @@ -0,0 +1,13 @@ +import fg from 'fast-glob'; +import type { PluginOption } from 'vite'; + +export const watchPublicPlugin = (): PluginOption => ({ + name: 'watch-public-plugin', + async buildStart() { + const files = await fg(['public/**/*']); + + for (const file of files) { + this.addWatchFile(file); + } + }, +}); diff --git a/packages/hmr/lib/plugins/watch-rebuild-plugin.ts b/packages/hmr/lib/plugins/watch-rebuild-plugin.ts new file mode 100644 index 0000000..0e80383 --- /dev/null +++ b/packages/hmr/lib/plugins/watch-rebuild-plugin.ts @@ -0,0 +1,68 @@ +import { BUILD_COMPLETE, LOCAL_RELOAD_SOCKET_URL } from '../consts.js'; +import MessageInterpreter from '../interpreter/index.js'; +import { WebSocket } from 'ws'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { PluginConfigType } from '../types.js'; +import type { PluginOption } from 'vite'; + +const injectionsPath = resolve(import.meta.dirname, '..', 'injections'); + +const refreshCode = readFileSync(resolve(injectionsPath, 'refresh.js'), 'utf-8'); +const reloadCode = readFileSync(resolve(injectionsPath, 'reload.js'), 'utf-8'); + +export const watchRebuildPlugin = (config: PluginConfigType): PluginOption => { + const { refresh, reload, id: _id, onStart } = config; + const hmrCode = (refresh ? refreshCode : '') + (reload ? reloadCode : ''); + + let ws: WebSocket | null = null; + + const id = _id ?? Math.random().toString(36); + let reconnectTries = 0; + + const initializeWebSocket = () => { + ws = new WebSocket(LOCAL_RELOAD_SOCKET_URL); + + ws.onopen = () => { + console.log(`[HMR] Connected to dev-server at ${LOCAL_RELOAD_SOCKET_URL}`); + }; + + ws.onerror = () => { + console.error(`[HMR] Failed to connect server at ${LOCAL_RELOAD_SOCKET_URL}`); + console.warn('Retrying in 3 seconds...'); + ws = null; + + if (reconnectTries <= 2) { + setTimeout(() => { + reconnectTries++; + initializeWebSocket(); + }, 3_000); + } else { + console.error(`[HMR] Cannot establish connection to server at ${LOCAL_RELOAD_SOCKET_URL}`); + } + }; + }; + + return { + name: 'watch-rebuild', + closeBundle() { + onStart?.(); + if (!ws) { + initializeWebSocket(); + return; + } + /** + * When the build is complete, send a message to the reload server. + * The reload server will send a message to the client to reload or refresh the extension. + */ + ws.send(MessageInterpreter.send({ type: BUILD_COMPLETE, id })); + }, + generateBundle(_options, bundle) { + for (const module of Object.values(bundle)) { + if (module.type === 'chunk') { + module.code = `(function() {let __HMR_ID = "${id}";\n` + hmrCode + '\n' + '})();' + '\n' + module.code; + } + } + }, + }; +}; diff --git a/packages/hmr/lib/types.ts b/packages/hmr/lib/types.ts new file mode 100644 index 0000000..c8eeae1 --- /dev/null +++ b/packages/hmr/lib/types.ts @@ -0,0 +1,18 @@ +import type { BUILD_COMPLETE, DO_UPDATE, DONE_UPDATE } from './consts.js'; + +type UpdateRequestMessageType = { + type: typeof DO_UPDATE; + id: string; +}; + +type UpdateCompleteMessageType = { type: typeof DONE_UPDATE }; +type BuildCompletionMessageType = { type: typeof BUILD_COMPLETE; id: string }; + +export type WebSocketMessageType = UpdateCompleteMessageType | UpdateRequestMessageType | BuildCompletionMessageType; + +export type PluginConfigType = { + onStart?: () => void; + reload?: boolean; + refresh?: boolean; + id?: string; +}; diff --git a/packages/hmr/package.json b/packages/hmr/package.json new file mode 100644 index 0000000..c9789d9 --- /dev/null +++ b/packages/hmr/package.json @@ -0,0 +1,36 @@ +{ + "name": "@extension/hmr", + "version": "0.5.0", + "description": "chrome extension - hot module reload/refresh", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist && pnpx rimraf build", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b && rollup --config dist/rollup.config.js", + "dev": "tsx lib/initializers/init-reload-server.ts", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@extension/env": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@rollup/plugin-sucrase": "^5.0.2", + "@types/ws": "^8.18.1", + "esbuild": "^0.25.4", + "esm": "^3.2.25", + "rollup": "^4.41.0", + "ts-node": "^10.9.2", + "ws": "^8.18.2" + } +} diff --git a/packages/hmr/rollup.config.ts b/packages/hmr/rollup.config.ts new file mode 100644 index 0000000..cb9f006 --- /dev/null +++ b/packages/hmr/rollup.config.ts @@ -0,0 +1,29 @@ +import sucrase from '@rollup/plugin-sucrase'; +import type { Plugin, RollupOptions } from 'rollup'; + +const plugins = [ + // @ts-expect-error Because of the lack of calling signature + sucrase({ + exclude: ['node_modules/**'], + transforms: ['typescript'], + }), +] satisfies Plugin[]; + +export default [ + { + plugins, + input: 'lib/injections/reload.ts', + output: { + format: 'esm', + file: 'dist/lib/injections/reload.js', + }, + }, + { + plugins, + input: 'lib/injections/refresh.ts', + output: { + format: 'esm', + file: 'dist/lib/injections/refresh.js', + }, + }, +] satisfies RollupOptions[]; diff --git a/packages/hmr/tsconfig.json b/packages/hmr/tsconfig.json new file mode 100644 index 0000000..a8fd99f --- /dev/null +++ b/packages/hmr/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "outDir": "dist", + "baseUrl": ".", + "types": ["chrome", "node"] + }, + "include": ["index.mts", "rollup.config.ts", "lib"] +} diff --git a/packages/i18n/.gitignore b/packages/i18n/.gitignore new file mode 100644 index 0000000..a1f12c3 --- /dev/null +++ b/packages/i18n/.gitignore @@ -0,0 +1 @@ +lib/i18n.ts \ No newline at end of file diff --git a/packages/i18n/README.md b/packages/i18n/README.md new file mode 100644 index 0000000..e6f654c --- /dev/null +++ b/packages/i18n/README.md @@ -0,0 +1,173 @@ +# I18n Package + +This package provides a set of tools to help you internationalize your Chrome Extension. + +https://developer.chrome.com/docs/extensions/reference/api/i18n + +## Installation + +If you want to use the i18n translation function in each pages, you need to add the following to the package.json file. + +```json +{ + "dependencies": { + "@extension/i18n": "workspace:*" + } +} +``` + +Then run the following command to install the package. + +```bash +pnpm install +``` + +## Manage translations + +You can manage translations in the `locales` directory. + +`locales/en/messages.json` + +```json +{ + "helloWorld": { + "message": "Hello, World!" + } +} +``` + +`locales/ko/messages.json` + +```json +{ + "helloWorld": { + "message": "안녕하세요, 여러분!" + } +} +``` + +## Add a new language + +Create folder inside `locales` with name from [languages](https://developer.chrome.com/docs/extensions/reference/api/i18n?hl=pl#support_multiple_languages), which need include `message.json` file. + +## Usage + +### Translation function + +Just import the `t` function and use it to translate the key. + +```typescript +import { t } from '@extension/i18n'; + +console.log(t('loading')); // Loading... +``` + +```typescript jsx +import { t } from '@extension/i18n'; + +const Component = () => { + return ( + + ); +}; +``` + +### Placeholders + +If you want to use placeholders, you can use the following format. + +> For more information, see the [Message Placeholders](https://developer.chrome.com/docs/extensions/how-to/ui/localization-message-formats#placeholders) section. + +`locales/en/messages.json` + +```json +{ + "greeting": { + "description": "Greeting message", + "message": "Hello, My name is $NAME$", + "placeholders": { + "name": { + "content": "$1", + "example": "John Doe" + } + } + }, + "hello": { + "description": "Placeholder example", + "message": "Hello $1" + } +} +``` + +`locales/ko/messages.json` + +```json +{ + "greeting": { + "description": "인사 메시지", + "message": "안녕하세요, 제 이름은 $NAME$입니다.", + "placeholders": { + "name": { + "content": "$1", + "example": "서종학" + } + } + }, + "hello": { + "description": "Placeholder 예시", + "message": "안녕 $1" + } +} +``` + +If you want to replace the placeholder, you can pass the value as the second argument. + +Function `t` has exactly the same interface as the `chrome.i18n.getMessage` function. + +```typescript +import { t } from '@extension/i18n'; + +console.log(t('greeting', 'John Doe')); // Hello, My name is John Doe +console.log(t('greeting', ['John Doe'])); // Hello, My name is John Doe + +console.log(t('hello')); // Hello +console.log(t('hello', 'World')); // Hello World +console.log(t('hello', ['World'])); // Hello World +``` + +### Locale setting on development + +If you want to enforce displaying specific language, you need to set `CEB_DEV_LOCALE` in `.env` file (work only for development). + +### Type Safety + +When you forget to add a key to all language's `messages.json` files, you will get a Typescript error. + +`locales/en/messages.json` + +```json +{ + "hello": { + "message": "Hello World!" + } +} +``` + +`locales/ko/messages.json` + +```json +{ + "helloWorld": { + "message": "안녕하세요, 여러분!" + } +} +``` + +```typescript +import { t } from '@extension/i18n'; + +// Error: TS2345: Argument of type "hello" is not assignable to parameter of type +console.log(t('hello')); +``` diff --git a/packages/i18n/index.mts b/packages/i18n/index.mts new file mode 100644 index 0000000..09c185e --- /dev/null +++ b/packages/i18n/index.mts @@ -0,0 +1 @@ +export * from './lib/index.js'; diff --git a/packages/i18n/lib/consts.ts b/packages/i18n/lib/consts.ts new file mode 100644 index 0000000..5db4bd9 --- /dev/null +++ b/packages/i18n/lib/consts.ts @@ -0,0 +1,62 @@ +/** + * @url https://developer.chrome.com/docs/extensions/reference/api/i18n#support_multiple_languages + */ +export const SUPPORTED_LANGUAGES = { + ar: 'Arabic', + am: 'Amharic', + bg: 'Bulgarian', + bn: 'Bengali', + ca: 'Catalan', + cs: 'Czech', + da: 'Danish', + de: 'German', + el: 'Greek', + en: 'English', + en_AU: 'English (Australia)', + en_GB: 'English (Great Britain)', + en_US: 'English (USA)', + es: 'Spanish', + es_419: 'Spanish (Latin America and Caribbean)', + et: 'Estonian', + fa: 'Persian', + fi: 'Finnish', + fil: 'Filipino', + fr: 'French', + gu: 'Gujarati', + he: 'Hebrew', + hi: 'Hindi', + hr: 'Croatian', + hu: 'Hungarian', + id: 'Indonesian', + it: 'Italian', + ja: 'Japanese', + kn: 'Kannada', + ko: 'Korean', + lt: 'Lithuanian', + lv: 'Latvian', + ml: 'Malayalam', + mr: 'Marathi', + ms: 'Malay', + nl: 'Dutch', + no: 'Norwegian', + pl: 'Polish', + pt_BR: 'Portuguese (Brazil)', + pt_PT: 'Portuguese (Portugal)', + ro: 'Romanian', + ru: 'Russian', + sk: 'Slovak', + sl: 'Slovenian', + sr: 'Serbian', + sv: 'Swedish', + sw: 'Swahili', + ta: 'Tamil', + te: 'Telugu', + th: 'Thai', + tr: 'Turkish', + uk: 'Ukrainian', + vi: 'Vietnamese', + zh_CN: 'Chinese (China)', + zh_TW: 'Chinese (Taiwan)', +} as const; + +export const I18N_FILE_PATH = './lib/i18n.ts'; diff --git a/packages/i18n/lib/i18n-dev.ts b/packages/i18n/lib/i18n-dev.ts new file mode 100644 index 0000000..7fa8caf --- /dev/null +++ b/packages/i18n/lib/i18n-dev.ts @@ -0,0 +1,34 @@ +// IT WILL BE ADJUSTED TO YOUR LANGUAGE DURING BUILD TIME, DON'T MOVE BELOW IMPORT TO OTHER LINE +import localeJSON from '../locales/en/messages.json' with { type: 'json' }; +import type { I18nValueType, LocalesJSONType } from './types.js'; + +const translate = (key: keyof LocalesJSONType, substitutions?: string | string[]) => { + const localeValues = localeJSON[key] as I18nValueType; + let message = localeValues.message; + /** + * This is a placeholder replacement logic. But it's not perfect. + * It just imitates the behavior of the Chrome extension i18n API. + * Please check the official document for more information And double-check the behavior on production build. + * + * @url https://developer.chrome.com/docs/extensions/how-to/ui/localization-message-formats#placeholders + */ + if (localeValues.placeholders) { + Object.entries(localeValues.placeholders).forEach(([key, { content }]) => { + if (content) { + message = message.replace(new RegExp(`\\$${key}\\$`, 'gi'), content); + } + }); + } + + if (!substitutions) { + return message; + } else if (Array.isArray(substitutions)) { + return substitutions.reduce((acc, cur, idx) => acc.replace(`$${idx++}`, cur), message); + } + + return message.replace(/\$(\d+)/, substitutions); +}; + +const removePlaceholder = (message: string) => message.replace(/\$\d+/g, ''); + +export const t = (...args: Parameters) => removePlaceholder(translate(...args)); diff --git a/packages/i18n/lib/i18n-prod.ts b/packages/i18n/lib/i18n-prod.ts new file mode 100644 index 0000000..958b2ee --- /dev/null +++ b/packages/i18n/lib/i18n-prod.ts @@ -0,0 +1,3 @@ +import type { MessageKeyType } from './types.js'; + +export const t = (key: MessageKeyType, substitutions?: string | string[]) => chrome.i18n.getMessage(key, substitutions); diff --git a/packages/i18n/lib/index.ts b/packages/i18n/lib/index.ts new file mode 100644 index 0000000..b6f9a1d --- /dev/null +++ b/packages/i18n/lib/index.ts @@ -0,0 +1,4 @@ +import { t as t_dev_or_prod } from './i18n.js'; +import type { t as t_dev } from './i18n-dev.js'; + +export const t = t_dev_or_prod as unknown as typeof t_dev; diff --git a/packages/i18n/lib/prepare-build.ts b/packages/i18n/lib/prepare-build.ts new file mode 100644 index 0000000..641bb88 --- /dev/null +++ b/packages/i18n/lib/prepare-build.ts @@ -0,0 +1,22 @@ +import setRelatedLocaleImports from './set-related-locale-import.js'; +import { IS_DEV } from '@extension/env'; +import { cpSync, existsSync, mkdirSync } from 'node:fs'; +import { resolve } from 'node:path'; + +(() => { + const i18nPath = IS_DEV ? 'lib/i18n-dev.ts' : 'lib/i18n-prod.ts'; + cpSync(i18nPath, resolve('lib', 'i18n.ts')); + + const outDir = resolve(import.meta.dirname, '..', '..', '..', '..', 'dist'); + if (!existsSync(outDir)) { + mkdirSync(outDir); + } + + const localePath = resolve(outDir, '_locales'); + cpSync(resolve('locales'), localePath, { recursive: true }); + + if (IS_DEV) { + setRelatedLocaleImports(); + } + console.log('I18n build complete'); +})(); diff --git a/packages/i18n/lib/set-related-locale-import.ts b/packages/i18n/lib/set-related-locale-import.ts new file mode 100644 index 0000000..c4214c5 --- /dev/null +++ b/packages/i18n/lib/set-related-locale-import.ts @@ -0,0 +1,38 @@ +import { I18N_FILE_PATH } from './consts.js'; +import { lstatSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { SupportedLanguagesKeysType, SupportedLanguagesWithoutRegionKeysType } from './types.js'; + +export default () => { + const locale = Intl.DateTimeFormat().resolvedOptions().locale.replace('-', '_') as SupportedLanguagesKeysType; + const localeWithoutRegion = locale.split('_')[0] as SupportedLanguagesWithoutRegionKeysType; + + const localesDir = resolve(import.meta.dirname, '..', '..', 'locales'); + const readLocalesFolder = readdirSync(localesDir); + + const implementedLocales = readLocalesFolder.map(innerDir => { + if (lstatSync(resolve(localesDir, innerDir)).isDirectory()) { + return innerDir; + } + return; + }); + + const i18nFileSplitContent = readFileSync(I18N_FILE_PATH, 'utf-8').split('\n'); + + if (process.env['CEB_DEV_LOCALE']) { + i18nFileSplitContent[1] = `import localeJSON from '../locales/${process.env['CEB_DEV_LOCALE']}/messages.json' with { type: 'json' };`; + } else { + if (implementedLocales.includes(locale)) { + i18nFileSplitContent[1] = `import localeJSON from '../locales/${locale}/messages.json' with { type: 'json' };`; + } else if (implementedLocales.includes(localeWithoutRegion)) { + i18nFileSplitContent[1] = `import localeJSON from '../locales/${localeWithoutRegion}/messages.json' with { type: 'json' };`; + } else { + i18nFileSplitContent[1] = `import localeJSON from '../locales/en/messages.json' with { type: 'json' };`; + } + } + + // Join lines back together + const updatedI18nFile = i18nFileSplitContent.join('\n'); + + writeFileSync(I18N_FILE_PATH, updatedI18nFile, 'utf-8'); +}; diff --git a/packages/i18n/lib/types.ts b/packages/i18n/lib/types.ts new file mode 100644 index 0000000..886b4cc --- /dev/null +++ b/packages/i18n/lib/types.ts @@ -0,0 +1,12 @@ +import enMessage from '../locales/en/messages.json' with { type: 'json' }; +import type { SUPPORTED_LANGUAGES } from './consts.js'; + +export type SupportedLanguagesKeysType = keyof typeof SUPPORTED_LANGUAGES; +export type SupportedLanguagesWithoutRegionKeysType = Exclude; +export type I18nValueType = { + message: string; + placeholders?: Record; +}; + +export type MessageKeyType = keyof typeof enMessage; +export type LocalesJSONType = typeof enMessage; diff --git a/packages/i18n/locales/en/messages.json b/packages/i18n/locales/en/messages.json new file mode 100644 index 0000000..dddd6ef --- /dev/null +++ b/packages/i18n/locales/en/messages.json @@ -0,0 +1,45 @@ +{ + "extensionDescription": { + "description": "Extension description", + "message": "Chrome extension boilerplate developed with Vite, React and Typescript" + }, + "extensionName": { + "description": "Extension name", + "message": "Chrome extension boilerplate" + }, + "toggleTheme": { + "message": "Toggle theme" + }, + "injectButton": { + "message": "Click to inject Content Scripts" + }, + "greeting": { + "description": "Greeting message", + "message": "Hello, My name is $NAME$", + "placeholders": { + "name": { + "content": "$1", + "example": "John Doe" + } + } + }, + "hello": { + "description": "Placeholder example", + "message": "Hello $1" + }, + "displayErrorInfo": { + "message": "Error occur" + }, + "displayErrorDescription": { + "message": "Sorry, something went wrong while loading the page." + }, + "displayErrorDetailsInfo": { + "message": "Error details:" + }, + "displayErrorUnknownErrorInfo": { + "message": "Unknown error" + }, + "displayErrorReset": { + "message": "Run Again" + } +} diff --git a/packages/i18n/locales/ko/messages.json b/packages/i18n/locales/ko/messages.json new file mode 100644 index 0000000..6e7f634 --- /dev/null +++ b/packages/i18n/locales/ko/messages.json @@ -0,0 +1,45 @@ +{ + "extensionDescription": { + "description": "Extension description", + "message": "React, Typescript, Vite를 사용한 크롬 익스텐션 보일러플레이트입니다." + }, + "extensionName": { + "description": "Extension name", + "message": "크롬 익스텐션 보일러플레이트" + }, + "toggleTheme": { + "message": "테마 변경" + }, + "injectButton": { + "message": "콘텐츠 스크립트를 삽입하려면 클릭하세요." + }, + "greeting": { + "description": "인사 메시지", + "message": "안녕하세요, 제 이름은 $NAME$입니다.", + "placeholders": { + "name": { + "content": "$1", + "example": "홍길동" + } + } + }, + "hello": { + "description": "Placeholder 예시", + "message": "안녕하세요, $1님." + }, + "displayErrorInfo": { + "message": "오류 발생" + }, + "displayErrorDescription": { + "message": "죄송합니다. 페이지를 로드하는 중 문제가 발생했습니다." + }, + "displayErrorDetailsInfo": { + "message": "오류 세부 정보:" + }, + "displayErrorUnknownErrorInfo": { + "message": "알 수 없는 오류" + }, + "displayErrorReset": { + "message": "다시 실행합니다." + } +} diff --git a/packages/i18n/package.json b/packages/i18n/package.json new file mode 100644 index 0000000..f6edad2 --- /dev/null +++ b/packages/i18n/package.json @@ -0,0 +1,30 @@ +{ + "name": "@extension/i18n", + "version": "0.5.0", + "description": "chrome extension - internationalization", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "main": "dist/index.mjs", + "types": "index.mts", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b prepare-build.tsconfig.json && node --env-file=../../.env dist/lib/prepare-build.js && tsc -b", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/env": "workspace:*" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*" + } +} diff --git a/packages/i18n/prepare-build.tsconfig.json b/packages/i18n/prepare-build.tsconfig.json new file mode 100644 index 0000000..4094a3c --- /dev/null +++ b/packages/i18n/prepare-build.tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["lib/prepare-build.ts"] +} diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/i18n/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/packages/module-manager/README.md b/packages/module-manager/README.md new file mode 100644 index 0000000..26ace83 --- /dev/null +++ b/packages/module-manager/README.md @@ -0,0 +1,64 @@ +# Module manager + +Module manager is a tool to manage modules in a project. It can be used to delete or recover some pages. + +## Usage + +### On Root + +```bash +$ pnpm module-manager +``` + +OR + +```bash +pnpm module-manager -d popup +``` + +### On Module + +```bash +$ pnpm start +``` + +OR + +```bash +pnpm start -d popup +``` + +> [!NOTE] +> For complete info about CLI support run: +> `pnpm module-manager --help` + +> [!IMPORTANT] +> If you want to remove all tests, and something else with one command, you need to set `tests` as first argument like: +> `pnpm module-manager -d tests popup ...` + +### Choose a tool + +``` +? Choose a tool (Use arrow keys) +❯ Delete Feature + Recover Feature +``` + +## How it works + +### Delete Feature + +When you select an unused module, Module Manager compresses the contents of that folder, takes a snapshot of it, and removes it. + +It also automatically removes anything that needs to be cleared from the manifest. + +### Recover Feature + +When you select a module that has been deleted, The Module Manager will recover that module from the snapshot and add it back to the manifest. + +> [!IMPORTANT] +> SAVE ALL FILES FROM /ARCHIVE, WITHOUT THESE FILES' RECOVERING PROCESS WON'T WORK + +> [!IMPORTANT] +> IF YOU DECIDE TO REMOVE ONE OF CONTENT SCRIPTs AFTER EDIT, E.G YOU REMOVE OR ADD ANY MATCHES, +> REMEMBER TO EDIT ALSO [MODULE_CONFIG](./lib/const.ts) TO BE ABLE TO RECOVER IT EASILY IN THE FUTURE. diff --git a/packages/module-manager/index.mts b/packages/module-manager/index.mts new file mode 100644 index 0000000..b33470e --- /dev/null +++ b/packages/module-manager/index.mts @@ -0,0 +1,3 @@ +import { startManager } from './lib/index.js'; + +startManager(); diff --git a/packages/module-manager/lib/base/cli-args-processor.ts b/packages/module-manager/lib/base/cli-args-processor.ts new file mode 100644 index 0000000..ebc1748 --- /dev/null +++ b/packages/module-manager/lib/base/cli-args-processor.ts @@ -0,0 +1,51 @@ +import { CLI_OPTIONS, DEFAULT_CHOICES_VALUES, HELP_EXAMPLES } from '../const.js'; +import { checkCliArgsIsValid } from '../helpers/utils.js'; +import { excludeValuesFromBaseArray } from '@extension/shared'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import type { ICLIOptions, ModuleNameType } from '../types.js'; +import type { WritableDeep } from '@extension/shared'; + +export const processCLIArgs = (): ICLIOptions | null => { + const argv = yargs(hideBin(process.argv)) + .option('delete', CLI_OPTIONS[0]) + .option('recover', CLI_OPTIONS[1]) + .option('delete-exclude', CLI_OPTIONS[2]) + .option('recover-exclude', CLI_OPTIONS[3]) + .check(argv => checkCliArgsIsValid(argv)) + .strict() + .example(HELP_EXAMPLES as WritableDeep) + .help() + .showHelpOnFail(true) + .parseSync(); + + if (argv.delete) { + return { + action: 'delete', + targets: argv.delete as ModuleNameType[], + }; + } + + if (argv.recover) { + return { + action: 'recover', + targets: argv.recover as ModuleNameType[], + }; + } + + if (argv.deleteExclude) { + return { + action: 'delete', + targets: excludeValuesFromBaseArray(DEFAULT_CHOICES_VALUES, argv.deleteExclude), + }; + } + + if (argv.recoverExclude) { + return { + action: 'recover', + targets: excludeValuesFromBaseArray(DEFAULT_CHOICES_VALUES, argv.recoverExclude), + }; + } + + return null; +}; diff --git a/packages/module-manager/lib/base/index.ts b/packages/module-manager/lib/base/index.ts new file mode 100644 index 0000000..3e1d538 --- /dev/null +++ b/packages/module-manager/lib/base/index.ts @@ -0,0 +1,2 @@ +export * from './cli-args-processor.js'; +export * from './run-module-manager.js'; diff --git a/packages/module-manager/lib/base/run-module-manager.ts b/packages/module-manager/lib/base/run-module-manager.ts new file mode 100644 index 0000000..f3a7cdd --- /dev/null +++ b/packages/module-manager/lib/base/run-module-manager.ts @@ -0,0 +1,49 @@ +import manifest from '../../../../chrome-extension/manifest.js'; +import { MANAGER_ACTION_PROMPT_CONFIG } from '../const.js'; +import { promptSelection } from '../helpers/utils.js'; +import { deleteFeature, recoverFeature } from '../processing/index.js'; +import { execSync } from 'node:child_process'; +import { readFileSync, writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { CliActionType, ModuleNameType } from '../types.js'; +import type { ManifestType } from '@extension/shared'; + +const manifestPath = resolve(import.meta.dirname, '..', '..', '..', '..', 'chrome-extension', 'manifest.ts'); + +const manifestObject = JSON.parse(JSON.stringify(manifest)) as ManifestType; +const manifestString = readFileSync(manifestPath, 'utf-8'); + +export const runModuleManager = async (moduleName?: ModuleNameType, action?: CliActionType, isLastLap = true) => { + if (!action) { + action = (await promptSelection(MANAGER_ACTION_PROMPT_CONFIG)) as Awaited; + } + + switch (action) { + case 'delete': + await deleteFeature(manifestObject, moduleName); + break; + case 'recover': + await recoverFeature(manifestObject, moduleName); + } + + const updatedManifest = manifestString + .replace( + /const manifest = {[\s\S]*?} satisfies/, + 'const manifest = ' + JSON.stringify(manifestObject, null, 2) + ' satisfies', + ) + .replace(/ {2}"version": "[\s\S]*?",/, ' version: packageJson.version,'); + + writeFileSync(manifestPath, updatedManifest); + + if (isLastLap) { + execSync('pnpm i', { + stdio: 'inherit', + cwd: resolve('..', '..'), + }); + + execSync('pnpm -F chrome-extension lint:fix', { + stdio: 'inherit', + cwd: resolve('..', '..'), + }); + } +}; diff --git a/packages/module-manager/lib/const.ts b/packages/module-manager/lib/const.ts new file mode 100644 index 0000000..53dc1f2 --- /dev/null +++ b/packages/module-manager/lib/const.ts @@ -0,0 +1,98 @@ +const CHOICE_QUESTION = 'Choose feature to'; +export const RECOVER_CHOICE_QUESTION = `${CHOICE_QUESTION} recover`; +export const DELETE_CHOICE_QUESTION = `${CHOICE_QUESTION} delete`; + +export const DEFAULT_CHOICES = [ + { name: 'Content Script (Execute JS on Web Page)', value: 'content' }, + { name: 'Content Script UI (Render Custom React Component on Web Page)', value: 'content-ui' }, + { name: 'Content Script Runtime (Inject JS on Specific Actions like Popup Click)', value: 'content-runtime' }, + { name: 'Background Script', value: 'background' }, + { name: 'New Tab Override', value: 'new-tab' }, + { name: 'Popup (On Extension Icon Click)', value: 'popup' }, + { name: 'DevTools (Include DevTools Panel)', value: 'devtools' }, + { name: 'Side Panel', value: 'side-panel' }, + { name: 'Options Page', value: 'options' }, + { name: 'All tests', value: 'tests' }, +] as const; + +export const DEFAULT_CHOICES_VALUES = DEFAULT_CHOICES.map(item => item.value); + +export const HELP_EXAMPLES = [ + ['-d content-ui content-runtime', 'Delete content-ui and content-runtime'], + ['--de content devtools', 'Delete everything exclude content and devtools'], + ['-r options side-panel', 'Recover options and side-panel'], + ['--re popup new-tab', 'Recover everything exclude popup and new-tab'], +] as const; + +export const CLI_OPTIONS = [ + { alias: 'd', type: 'array', description: 'Delete specified features' }, + { alias: 'r', type: 'array', description: 'Recover specified features' }, + { alias: 'de', type: 'array', description: 'Delete all features except specified' }, + { alias: 're', type: 'array', description: 'Recover all features except specified' }, +] as const; + +export const MANAGER_ACTION_PROMPT_CONFIG = { + message: 'Choose a tool', + choices: [ + { name: 'Delete Feature', value: 'delete' }, + { name: 'Recover Feature', value: 'recover' }, + ], +} as const; + +export const MODULE_CONFIG = { + content: { + content_scripts: [ + { + matches: ['http://*/*', 'https://*/*', ''], + js: ['content/all.iife.js'], + }, + { + matches: ['https://example.com/*'], + js: ['content/example.iife.js'], + }, + ], + }, + 'content-ui': { + content_scripts: [ + { + matches: ['http://*/*', 'https://*/*', ''], + js: ['content-ui/all.iife.js'], + }, + { + matches: ['https://example.com/*'], + js: ['content-ui/example.iife.js'], + }, + ], + }, + background: { + background: { + service_worker: 'background.js', + type: 'module', + }, + }, + 'new-tab': { + chrome_url_overrides: { + newtab: 'new-tab/index.html', + }, + }, + popup: { + action: { + default_popup: 'popup/index.html', + default_icon: 'icon-34.png', + }, + }, + devtools: { + devtools_page: 'devtools/index.html', + }, + 'side-panel': { + side_panel: { + default_path: 'side-panel/index.html', + }, + permissions: ['sidePanel'], + }, + options: { + options_page: 'options/index.html', + }, +} as const; + +export const EXIT_PROMPT_ERROR = 'ExitPromptError'; diff --git a/packages/module-manager/lib/helpers/index.ts b/packages/module-manager/lib/helpers/index.ts new file mode 100644 index 0000000..81b12c2 --- /dev/null +++ b/packages/module-manager/lib/helpers/index.ts @@ -0,0 +1,2 @@ +export * from './utils.js'; +export * from './zip-utils.js'; diff --git a/packages/module-manager/lib/helpers/utils.ts b/packages/module-manager/lib/helpers/utils.ts new file mode 100644 index 0000000..063bb37 --- /dev/null +++ b/packages/module-manager/lib/helpers/utils.ts @@ -0,0 +1,113 @@ +import { DEFAULT_CHOICES_VALUES, EXIT_PROMPT_ERROR, MODULE_CONFIG } from '../const.js'; +import { colorfulLog } from '@extension/shared'; +import { select } from '@inquirer/prompts'; +import { readdirSync } from 'node:fs'; +import type { DELETE_CHOICE_QUESTION, RECOVER_CHOICE_QUESTION } from '../const.js'; +import type { + ChoicesType, + CliEntriesType, + InputConfigType, + ModuleNameType, + WritableModuleConfigValuesType, +} from '../types.js'; +import type { ConditionalPickDeep, Entries, ManifestType } from '@extension/shared'; +import type { Arguments } from 'yargs'; + +export const isFolderEmpty = (path: string) => !readdirSync(path).length; + +export const promptSelection = async (inputConfig: InputConfigType) => + select(inputConfig).catch(err => { + if (err.name === EXIT_PROMPT_ERROR) { + process.exit(0); + } else { + colorfulLog(err.message, 'error'); + } + }) as Promise; + +export const processModuleConfig = ( + manifestObject: ManifestType, + moduleName: ModuleNameType, + isRecovering?: boolean, +) => { + if (moduleName === 'content-runtime' || moduleName === 'devtools-panel' || moduleName === 'tests') { + return; + } + + const moduleConfigValues = MODULE_CONFIG[moduleName]; + const moduleConfigEntriesOfKeys = Object.entries(moduleConfigValues) as Entries; + + if (moduleName === 'content' || moduleName === 'content-ui') { + if (isRecovering) { + (moduleConfigValues as WritableModuleConfigValuesType).content_scripts.map(script => + manifestObject.content_scripts?.push(script), + ); + } else { + const outputFileName = new RegExp(`${moduleName}/+`); + + manifestObject.content_scripts = manifestObject.content_scripts?.filter( + script => !outputFileName.test(script.js ? script.js[0] : ''), + ); + } + return; + } + + moduleConfigEntriesOfKeys.forEach(([key, value]) => { + const manifestValue = manifestObject[key]; + + if (manifestValue) { + if (manifestValue instanceof Array) { + const arrayValues = Object.values( + moduleConfigValues[key as ConditionalPickDeep], + ); + + if (isRecovering) { + manifestObject[key] = manifestValue.concat(arrayValues); + } else { + manifestObject[key] = manifestValue.filter((value: string) => !arrayValues.includes(value)); + } + } else { + delete manifestObject[key]; + } + } else if (isRecovering) { + Object.assign(manifestObject, { [key]: value }); + } else { + throw new Error(`Key ${key} not found in manifest.ts`); + } + }); +}; + +export const checkCliArgsIsValid = (argv: T) => { + const [key, values] = Object.entries(argv)[1] as CliEntriesType; + + if (Array.isArray(values)) { + for (const value of values) { + if (!DEFAULT_CHOICES_VALUES.some(moduleName => value === moduleName)) { + throw new Error(`All values after --${key} must be names of pages`); + } + } + } + + return true; +}; + +export const processSelection = async ( + choices: ChoicesType, + question: typeof RECOVER_CHOICE_QUESTION | typeof DELETE_CHOICE_QUESTION, + moduleName?: ModuleNameType, +) => { + if (!choices.length) { + colorfulLog('No options available', 'warning'); + process.exit(0); + } + + if (!moduleName) { + const inputConfig = { + message: question, + choices, + } as const; + + return (await promptSelection(inputConfig)) as Awaited; + } + + return null; +}; diff --git a/packages/module-manager/lib/helpers/zip-utils.ts b/packages/module-manager/lib/helpers/zip-utils.ts new file mode 100644 index 0000000..125f06e --- /dev/null +++ b/packages/module-manager/lib/helpers/zip-utils.ts @@ -0,0 +1,97 @@ +import { streamFileToZip } from '@extension/dev-utils'; +import fg from 'fast-glob'; +import { unzipSync, Zip } from 'fflate'; +import { rimraf } from 'rimraf'; +import { createWriteStream, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import type { ModuleNameType } from '../types.js'; + +const zipAndDeleteModuleTest = async (moduleName: ModuleNameType, archivePath: string, testsPath: string) => { + const moduleTestName = `page-${moduleName}.test.ts`; + const moduleTestsPath = resolve(testsPath, moduleTestName); + + await zipFolder(testsPath, resolve(archivePath, `${moduleName}.test.zip`), [moduleTestName]); + void rimraf([moduleTestsPath]); +}; + +export const zipAndDeleteModule = async ( + moduleName: ModuleNameType, + pagesPath: string, + archivePath: string, + testsPath?: string, +) => { + const modulePath = resolve(pagesPath, moduleName); + + await zipFolder(resolve(pagesPath, moduleName), resolve(archivePath, `${moduleName}.zip`)); + void rimraf([modulePath]); + + if (testsPath && existsSync(testsPath)) { + await zipAndDeleteModuleTest(moduleName, archivePath, testsPath); + } +}; + +export const zipAndDeleteTests = async (testsPath: string, archivePath: string) => { + await zipFolder(testsPath, resolve(archivePath, `tests.zip`)); + void rimraf(testsPath); +}; + +export const unZipAndDeleteModule = (zipFilePath: string, destPath: string) => { + const unzipped = unzipSync(readFileSync(zipFilePath)); + + mkdirSync(destPath, { recursive: true }); + + for (const [filePath, fileData] of Object.entries(unzipped)) { + const dir = dirname(filePath); + + mkdirSync(dir, { recursive: true }); + writeFileSync(filePath, fileData); + } + + unlinkSync(zipFilePath); +}; + +export const zipFolder = async (folderPath: string, out: string, filesToInclude?: string[]) => { + const fileList = await fg( + ['!node_modules', '!dist', '!**/*/node_modules', '!**/*/dist'].concat( + filesToInclude?.length ? filesToInclude : ['**/*'], + ), + { + cwd: folderPath, + onlyFiles: true, + }, + ); + const output = createWriteStream(out); + + return new Promise((resolvePromise, rejectPromise) => { + const zip = new Zip((err, data, final) => { + if (err) { + rejectPromise(err); + } else { + output.write(data); + if (final) { + output.end(); + resolvePromise(); + console.log(`Archive created at: ${out} for recovery`); + } + } + }); + + for (const file of fileList) { + const absPath = resolve(folderPath, file); + + console.log(`Archiving file: ${absPath}`); + streamFileToZip( + absPath, + absPath, + zip, + () => { + output.end(); + rejectPromise(new Error('Aborted')); + }, + rejectPromise, + ); + } + + zip.end(); + }); +}; diff --git a/packages/module-manager/lib/index.ts b/packages/module-manager/lib/index.ts new file mode 100644 index 0000000..36648f9 --- /dev/null +++ b/packages/module-manager/lib/index.ts @@ -0,0 +1,21 @@ +import { processCLIArgs, runModuleManager } from './base/index.js'; +import { colorfulLog, sleep } from '@extension/shared'; + +const cliOptions = processCLIArgs(); + +export const startManager = () => { + if (cliOptions) { + const targets = cliOptions.targets; + + (async () => { + for (const moduleName of targets) { + colorfulLog(`Processing module: ${moduleName}`, 'info'); + const isLastIndex = targets.at(-1) === moduleName; + await runModuleManager(moduleName, cliOptions.action, isLastIndex); + await sleep(500); + } + })(); + } else { + void runModuleManager(); + } +}; diff --git a/packages/module-manager/lib/paths.ts b/packages/module-manager/lib/paths.ts new file mode 100644 index 0000000..6aaa2c7 --- /dev/null +++ b/packages/module-manager/lib/paths.ts @@ -0,0 +1,6 @@ +import { resolve } from 'node:path'; + +export const pagesPath = resolve(import.meta.dirname, '..', '..', '..', 'pages'); +export const testsPath = resolve(pagesPath, '..', 'tests'); +export const specsPath = resolve(testsPath, 'e2e', 'specs'); +export const archivePath = resolve(import.meta.dirname, '..', 'archive'); diff --git a/packages/module-manager/lib/processing/delete-feature.ts b/packages/module-manager/lib/processing/delete-feature.ts new file mode 100644 index 0000000..26622d2 --- /dev/null +++ b/packages/module-manager/lib/processing/delete-feature.ts @@ -0,0 +1,34 @@ +import { deleteModule } from './modules-handler.js'; +import { DEFAULT_CHOICES, DELETE_CHOICE_QUESTION } from '../const.js'; +import { processSelection } from '../helpers/utils.js'; +import { pagesPath, testsPath } from '../paths.js'; +import { existsSync, readdirSync } from 'node:fs'; +import type { ChoicesType, ModuleNameType } from '../types.ts'; +import type { ManifestType } from '@extension/shared'; + +export const deleteFeature = async (manifestObject: ManifestType, moduleName?: ModuleNameType) => { + const pageFolders = readdirSync(pagesPath); + + const choices: ChoicesType = DEFAULT_CHOICES.filter(choice => { + if (choice.value === 'background') { + return !!manifestObject.background; + } else if (choice.value === 'tests') { + return existsSync(testsPath); + } + + return pageFolders.includes(choice.value); + }); + + const processResult = await processSelection(choices, DELETE_CHOICE_QUESTION, moduleName); + + if (processResult) { + moduleName = processResult; + } + + if (moduleName === 'devtools') { + await deleteModule(manifestObject, moduleName as ModuleNameType); + await deleteModule(manifestObject, 'devtools-panel'); + } else { + await deleteModule(manifestObject, moduleName as ModuleNameType); + } +}; diff --git a/packages/module-manager/lib/processing/index.ts b/packages/module-manager/lib/processing/index.ts new file mode 100644 index 0000000..042911b --- /dev/null +++ b/packages/module-manager/lib/processing/index.ts @@ -0,0 +1,3 @@ +export * from './delete-feature.js'; +export * from './recover-feature.js'; +export * from './modules-handler.js'; diff --git a/packages/module-manager/lib/processing/modules-handler.ts b/packages/module-manager/lib/processing/modules-handler.ts new file mode 100644 index 0000000..ccd3f93 --- /dev/null +++ b/packages/module-manager/lib/processing/modules-handler.ts @@ -0,0 +1,48 @@ +import { unZipAndDeleteModule, zipAndDeleteModule, zipAndDeleteTests } from '../helpers/index.js'; +import { isFolderEmpty, processModuleConfig } from '../helpers/utils.js'; +import { archivePath, pagesPath, specsPath, testsPath } from '../paths.js'; +import { colorfulLog } from '@extension/shared'; +import { existsSync, mkdirSync, rmdirSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { ModuleNameType } from '../types.ts'; +import type { ManifestType } from '@extension/shared'; + +export const recoverModule = (manifestObject: ManifestType, moduleName: ModuleNameType) => { + const zipFilePath = resolve(archivePath, `${moduleName}.zip`); + const zipTestFilePath = resolve(archivePath, `${moduleName}.test.zip`); + + if (!existsSync(zipFilePath)) { + colorfulLog(`No archive found for ${moduleName}`, 'info'); + process.exit(0); + } + + processModuleConfig(manifestObject, moduleName, true); + + unZipAndDeleteModule(zipFilePath, pagesPath); + + if (existsSync(zipTestFilePath)) { + unZipAndDeleteModule(zipTestFilePath, specsPath); + } + + colorfulLog(`Recovered: ${moduleName}`, 'info'); + + if (isFolderEmpty(archivePath)) { + rmdirSync(archivePath); + } +}; + +export const deleteModule = async (manifestObject: ManifestType, moduleName: ModuleNameType) => { + processModuleConfig(manifestObject, moduleName); + + if (!existsSync(archivePath)) { + mkdirSync(archivePath, { recursive: true }); + } + + if (moduleName === 'tests') { + await zipAndDeleteTests(testsPath, archivePath); + } else { + await zipAndDeleteModule(moduleName, pagesPath, archivePath, specsPath); + } + + colorfulLog(`Deleted: ${moduleName}`, 'info'); +}; diff --git a/packages/module-manager/lib/processing/recover-feature.ts b/packages/module-manager/lib/processing/recover-feature.ts new file mode 100644 index 0000000..8087ad2 --- /dev/null +++ b/packages/module-manager/lib/processing/recover-feature.ts @@ -0,0 +1,41 @@ +import { recoverModule } from './modules-handler.js'; +import { DEFAULT_CHOICES, RECOVER_CHOICE_QUESTION } from '../const.js'; +import { processSelection } from '../helpers/utils.js'; +import { archivePath } from 'lib/paths.js'; +import { rimraf } from 'rimraf'; +import { existsSync, readdirSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { ChoicesType, ModuleNameType } from '../types.ts'; +import type { ManifestType } from '@extension/shared'; + +export const recoverFeature = async (manifestObject: ManifestType, moduleName?: ModuleNameType) => { + const archiveFiles = existsSync(archivePath) ? readdirSync(archivePath) : []; + + const choices: ChoicesType = DEFAULT_CHOICES.filter(choice => { + if (choice.value === 'background') { + return !manifestObject.background; + } + + return archiveFiles.includes(`${choice.value}.zip`); + }); + + const processResult = await processSelection(choices, RECOVER_CHOICE_QUESTION, moduleName); + + if (processResult) { + moduleName = processResult; + } + + if (moduleName === 'tests') { + recoverModule(manifestObject, moduleName as ModuleNameType); + const testZipFilePath = resolve(archivePath, `${moduleName}.test.zip`); + + if (existsSync(testZipFilePath)) { + await rimraf(testZipFilePath); + } + } else if (moduleName === 'devtools') { + recoverModule(manifestObject, moduleName as ModuleNameType); + recoverModule(manifestObject, 'devtools-panel'); + } else { + recoverModule(manifestObject, moduleName as ModuleNameType); + } +}; diff --git a/packages/module-manager/lib/types.ts b/packages/module-manager/lib/types.ts new file mode 100644 index 0000000..0ff99df --- /dev/null +++ b/packages/module-manager/lib/types.ts @@ -0,0 +1,19 @@ +import type { DEFAULT_CHOICES, MODULE_CONFIG } from './const.js'; +import type { WritableDeep } from '@extension/shared'; +import type { select } from '@inquirer/prompts'; + +type ModuleConfigType = typeof MODULE_CONFIG; + +export type ChoiceType = (typeof DEFAULT_CHOICES)[number]; +export type ChoicesType = ChoiceType[]; +export type ModuleNameType = ChoiceType['value'] | 'devtools-panel'; +export type InputConfigType = Parameters[0]; +export type WritableModuleConfigValuesType = WritableDeep; + +export interface ICLIOptions { + action: 'delete' | 'recover'; + targets: ModuleNameType[]; +} + +export type CliEntriesType = [string, (string | number)[]][]; +export type CliActionType = 'delete' | 'recover'; diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json new file mode 100644 index 0000000..f81046e --- /dev/null +++ b/packages/module-manager/package.json @@ -0,0 +1,23 @@ +{ + "name": "@extension/module-manager", + "version": "0.5.0", + "description": "chrome extension - module manager", + "type": "module", + "private": true, + "sideEffects": true, + "scripts": { + "start": "tsx index.mts", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore" + }, + "devDependencies": { + "@extension/dev-utils": "workspace:*", + "@extension/shared": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@inquirer/prompts": "^7.5.1", + "@types/yargs": "^17.0.33", + "tsx": "^4.19.4", + "yargs": "^17.7.2" + } +} diff --git a/packages/module-manager/tsconfig.json b/packages/module-manager/tsconfig.json new file mode 100644 index 0000000..55ee95e --- /dev/null +++ b/packages/module-manager/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["lib", "index.mts"] +} diff --git a/packages/shared/README.md b/packages/shared/README.md new file mode 100644 index 0000000..7d40c22 --- /dev/null +++ b/packages/shared/README.md @@ -0,0 +1,12 @@ +# Shared Package + +This package contains code shared with other packages. +To use the code in the package, you need to add the following to the package.json file. + +```json +{ + "dependencies": { + "@extension/shared": "workspace:*" + } +} +``` diff --git a/packages/shared/const.ts b/packages/shared/const.ts new file mode 100644 index 0000000..7743b5f --- /dev/null +++ b/packages/shared/const.ts @@ -0,0 +1,3 @@ +export const PROJECT_URL_OBJECT = { + url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite', +} as const; diff --git a/packages/shared/index.mts b/packages/shared/index.mts new file mode 100644 index 0000000..336c729 --- /dev/null +++ b/packages/shared/index.mts @@ -0,0 +1,4 @@ +export * from './lib/hooks/index.js'; +export * from './lib/hoc/index.js'; +export * from './lib/utils/index.js'; +export * from './const.js'; diff --git a/packages/shared/lib/hoc/index.ts b/packages/shared/lib/hoc/index.ts new file mode 100644 index 0000000..564ea2c --- /dev/null +++ b/packages/shared/lib/hoc/index.ts @@ -0,0 +1,2 @@ +export { withSuspense } from './with-suspense.js'; +export { withErrorBoundary } from './with-error-boundary.js'; diff --git a/packages/shared/lib/hoc/with-error-boundary.tsx b/packages/shared/lib/hoc/with-error-boundary.tsx new file mode 100644 index 0000000..30dd980 --- /dev/null +++ b/packages/shared/lib/hoc/with-error-boundary.tsx @@ -0,0 +1,15 @@ +import { ErrorBoundary } from 'react-error-boundary'; +import type { ComponentType } from 'react'; +import type { FallbackProps } from 'react-error-boundary'; + +export const withErrorBoundary = >( + Component: ComponentType, + FallbackComponent: ComponentType, +) => + function WithErrorBoundary(props: T) { + return ( + + + + ); + }; diff --git a/packages/shared/lib/hoc/with-suspense.tsx b/packages/shared/lib/hoc/with-suspense.tsx new file mode 100644 index 0000000..2f673a0 --- /dev/null +++ b/packages/shared/lib/hoc/with-suspense.tsx @@ -0,0 +1,10 @@ +import { Suspense } from 'react'; +import type { ComponentType, ReactElement } from 'react'; + +export const withSuspense = + >(Component: ComponentType, SuspenseComponent: ReactElement) => + (props: T) => ( + + + + ); diff --git a/packages/shared/lib/hooks/index.ts b/packages/shared/lib/hooks/index.ts new file mode 100644 index 0000000..6f7e11e --- /dev/null +++ b/packages/shared/lib/hooks/index.ts @@ -0,0 +1 @@ +export * from './use-storage.js'; diff --git a/packages/shared/lib/hooks/use-storage.tsx b/packages/shared/lib/hooks/use-storage.tsx new file mode 100644 index 0000000..da50e83 --- /dev/null +++ b/packages/shared/lib/hooks/use-storage.tsx @@ -0,0 +1,56 @@ +import { useRef, useSyncExternalStore } from 'react'; +import type { BaseStorageType } from '@extension/storage'; + +type WrappedPromise = ReturnType; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const storageMap: Map, WrappedPromise> = new Map(); + +const wrapPromise = (promise: Promise) => { + let status = 'pending'; + let result: R; + + const suspender = promise.then( + r => { + status = 'success'; + result = r; + }, + e => { + status = 'error'; + result = e; + }, + ); + + return { + read() { + switch (status) { + case 'pending': + throw suspender; + case 'error': + throw result; + default: + return result; + } + }, + }; +}; + +export const useStorage = < + Storage extends BaseStorageType, + Data = Storage extends BaseStorageType ? Data : unknown, +>( + storage: Storage, +) => { + const initializedRef = useRef(false); + const _data = useSyncExternalStore(storage.subscribe, storage.getSnapshot); + + if (!storageMap.has(storage)) { + storageMap.set(storage, wrapPromise(storage.get())); + } + + if (_data || initializedRef.current) { + storageMap.set(storage, { read: () => _data }); + initializedRef.current = true; + } + + return (_data ?? storageMap.get(storage)!.read()) as Exclude>; +}; diff --git a/packages/shared/lib/utils/colorful-logger.ts b/packages/shared/lib/utils/colorful-logger.ts new file mode 100644 index 0000000..ccd2833 --- /dev/null +++ b/packages/shared/lib/utils/colorful-logger.ts @@ -0,0 +1,27 @@ +import { COLORS } from './const.js'; +import type { ColorType, ValueOf } from './types.js'; + +export const colorfulLog = (message: string, type: ColorType) => { + let color: ValueOf; + + switch (type) { + case 'success': + color = COLORS.FgGreen; + break; + case 'info': + color = COLORS.FgBlue; + break; + case 'error': + color = COLORS.FgRed; + break; + case 'warning': + color = COLORS.FgYellow; + break; + default: + color = COLORS[type]; + break; + } + + console.info(color, message); + console.info(COLORS['Reset']); +}; diff --git a/packages/shared/lib/utils/const.ts b/packages/shared/lib/utils/const.ts new file mode 100644 index 0000000..e69d6a2 --- /dev/null +++ b/packages/shared/lib/utils/const.ts @@ -0,0 +1,25 @@ +export const COLORS = { + Reset: '\x1b[0m', + Bright: '\x1b[1m', + Dim: '\x1b[2m', + Underscore: '\x1b[4m', + Blink: '\x1b[5m', + Reverse: '\x1b[7m', + Hidden: '\x1b[8m', + FgBlack: '\x1b[30m', + FgRed: '\x1b[31m', + FgGreen: '\x1b[32m', + FgYellow: '\x1b[33m', + FgBlue: '\x1b[34m', + FgMagenta: '\x1b[35m', + FgCyan: '\x1b[36m', + FgWhite: '\x1b[37m', + BgBlack: '\x1b[40m', + BgRed: '\x1b[41m', + BgGreen: '\x1b[42m', + BgYellow: '\x1b[43m', + BgBlue: '\x1b[44m', + BgMagenta: '\x1b[45m', + BgCyan: '\x1b[46m', + BgWhite: '\x1b[47m', +} as const; diff --git a/packages/shared/lib/utils/helpers.ts b/packages/shared/lib/utils/helpers.ts new file mode 100644 index 0000000..955b9e6 --- /dev/null +++ b/packages/shared/lib/utils/helpers.ts @@ -0,0 +1,8 @@ +import type { ExcludeValuesFromBaseArrayType } from './types.js'; + +export const excludeValuesFromBaseArray = ( + baseArray: B, + excludeArray: E, +) => baseArray.filter(value => !excludeArray.includes(value)) as ExcludeValuesFromBaseArrayType; + +export const sleep = async (time: number) => new Promise(r => setTimeout(r, time)); diff --git a/packages/shared/lib/utils/index.ts b/packages/shared/lib/utils/index.ts new file mode 100644 index 0000000..981861d --- /dev/null +++ b/packages/shared/lib/utils/index.ts @@ -0,0 +1,4 @@ +export * from './helpers.js'; +export * from './colorful-logger.js'; +export * from './init-app-with-shadow.js'; +export type * from './types.js'; diff --git a/packages/shared/lib/utils/init-app-with-shadow.ts b/packages/shared/lib/utils/init-app-with-shadow.ts new file mode 100644 index 0000000..bb3963e --- /dev/null +++ b/packages/shared/lib/utils/init-app-with-shadow.ts @@ -0,0 +1,34 @@ +import { createRoot } from 'react-dom/client'; +import type { ReactElement } from 'react'; + +export const initAppWithShadow = ({ id, app, inlineCss }: { id: string; inlineCss: string; app: ReactElement }) => { + const root = document.createElement('div'); + root.id = id; + + document.body.append(root); + + const rootIntoShadow = document.createElement('div'); + rootIntoShadow.id = `shadow-root-${id}`; + + const shadowRoot = root.attachShadow({ mode: 'open' }); + + if (navigator.userAgent.includes('Firefox')) { + /** + * In the firefox environment, adoptedStyleSheets cannot be used due to the bug + * @url https://bugzilla.mozilla.org/show_bug.cgi?id=1770592 + * + * Injecting styles into the document, this may cause style conflicts with the host page + */ + const styleElement = document.createElement('style'); + styleElement.innerHTML = inlineCss; + shadowRoot.appendChild(styleElement); + } else { + /** Inject styles into shadow dom */ + const globalStyleSheet = new CSSStyleSheet(); + globalStyleSheet.replaceSync(inlineCss); + shadowRoot.adoptedStyleSheets = [globalStyleSheet]; + } + + shadowRoot.appendChild(rootIntoShadow); + createRoot(rootIntoShadow).render(app); +}; diff --git a/packages/shared/lib/utils/types.ts b/packages/shared/lib/utils/types.ts new file mode 100644 index 0000000..cee05aa --- /dev/null +++ b/packages/shared/lib/utils/types.ts @@ -0,0 +1,10 @@ +import type { COLORS } from './const.js'; +import type { TupleToUnion } from 'type-fest'; + +export type * from 'type-fest'; +export type ColorType = 'success' | 'info' | 'error' | 'warning' | keyof typeof COLORS; +export type ExcludeValuesFromBaseArrayType = Exclude< + TupleToUnion, + TupleToUnion +>[]; +export type ManifestType = chrome.runtime.ManifestV3; diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..8ef7721 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,33 @@ +{ + "name": "@extension/shared", + "version": "0.5.0", + "description": "chrome extension - shared code", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit", + "postinstall": "pnpm ready" + }, + "dependencies": { + "react-error-boundary": "^6.0.0" + }, + "devDependencies": { + "@extension/storage": "workspace:*", + "@extension/tsconfig": "workspace:*", + "type-fest": "^4.41.0" + } +} diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/packages/storage/index.mts b/packages/storage/index.mts new file mode 100644 index 0000000..09c185e --- /dev/null +++ b/packages/storage/index.mts @@ -0,0 +1 @@ +export * from './lib/index.js'; diff --git a/packages/storage/lib/base/base.ts b/packages/storage/lib/base/base.ts new file mode 100644 index 0000000..a9870f8 --- /dev/null +++ b/packages/storage/lib/base/base.ts @@ -0,0 +1,157 @@ +import { SessionAccessLevelEnum, StorageEnum } from './enums.js'; +import type { BaseStorageType, StorageConfigType, ValueOrUpdateType } from './types.js'; + +/** + * Chrome reference error while running `processTailwindFeatures` in tailwindcss. + * To avoid this, we need to check if globalThis.chrome is available and add fallback logic. + */ +const chrome = globalThis.chrome; + +/** + * Sets or updates an arbitrary cache with a new value or the result of an update function. + */ +const updateCache = async (valueOrUpdate: ValueOrUpdateType, cache: D | null): Promise => { + // Type guard to check if our value or update is a function + const isFunction = (value: ValueOrUpdateType): value is (prev: D) => D | Promise => + typeof value === 'function'; + + // Type guard to check in case of a function if it's a Promise + const returnsPromise = (func: (prev: D) => D | Promise): func is (prev: D) => Promise => + // Use ReturnType to infer the return type of the function and check if it's a Promise + (func as (prev: D) => Promise) instanceof Promise; + if (isFunction(valueOrUpdate)) { + // Check if the function returns a Promise + if (returnsPromise(valueOrUpdate)) { + return valueOrUpdate(cache as D); + } else { + return valueOrUpdate(cache as D); + } + } else { + return valueOrUpdate; + } +}; + +/** + * If one session storage needs access from content scripts, we need to enable it globally. + * @default false + */ +let globalSessionAccessLevelFlag: StorageConfigType['sessionAccessForContentScripts'] = false; + +/** + * Checks if the storage permission is granted in the manifest.json. + */ +const checkStoragePermission = (storageEnum: StorageEnum): void => { + if (!chrome) { + return; + } + + if (!chrome.storage[storageEnum]) { + throw new Error(`"storage" permission in manifest.ts: "storage ${storageEnum}" isn't defined`); + } +}; + +/** + * Creates a storage area for persisting and exchanging data. + */ +export const createStorage = ( + key: string, + fallback: D, + config?: StorageConfigType, +): BaseStorageType => { + let cache: D | null = null; + let initialCache = false; + let listeners: Array<() => void> = []; + + const storageEnum = config?.storageEnum ?? StorageEnum.Local; + const liveUpdate = config?.liveUpdate ?? false; + + const serialize = config?.serialization?.serialize ?? ((v: D) => v); + const deserialize = config?.serialization?.deserialize ?? (v => v as D); + + // Set global session storage access level for StoryType.Session, only when not already done but needed. + if ( + globalSessionAccessLevelFlag === false && + storageEnum === StorageEnum.Session && + config?.sessionAccessForContentScripts === true + ) { + checkStoragePermission(storageEnum); + + chrome?.storage[storageEnum] + .setAccessLevel({ + accessLevel: SessionAccessLevelEnum.ExtensionPagesAndContentScripts, + }) + .catch(error => { + console.error(error); + console.error('Please call .setAccessLevel() into different context, like a background script.'); + }); + globalSessionAccessLevelFlag = true; + } + + // Register life cycle methods + const get = async (): Promise => { + checkStoragePermission(storageEnum); + const value = await chrome?.storage[storageEnum].get([key]); + + if (!value) { + return fallback; + } + + return deserialize(value[key]) ?? fallback; + }; + + const set = async (valueOrUpdate: ValueOrUpdateType) => { + if (!initialCache) { + cache = await get(); + } + cache = await updateCache(valueOrUpdate, cache); + + await chrome?.storage[storageEnum].set({ [key]: serialize(cache) }); + _emitChange(); + }; + + const subscribe = (listener: () => void) => { + listeners = [...listeners, listener]; + + return () => { + listeners = listeners.filter(l => l !== listener); + }; + }; + + const getSnapshot = () => cache; + + const _emitChange = () => { + listeners.forEach(listener => listener()); + }; + + // Listener for live updates from the browser + const _updateFromStorageOnChanged = async (changes: { [key: string]: chrome.storage.StorageChange }) => { + // Check if the key we are listening for is in the changes object + if (changes[key] === undefined) return; + + const valueOrUpdate: ValueOrUpdateType = deserialize(changes[key].newValue); + + if (cache === valueOrUpdate) return; + + cache = await updateCache(valueOrUpdate, cache); + + _emitChange(); + }; + + get().then(data => { + cache = data; + initialCache = true; + _emitChange(); + }); + + // Register listener for live updates for our storage area + if (liveUpdate) { + chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged); + } + + return { + get, + set, + getSnapshot, + subscribe, + }; +}; diff --git a/packages/storage/lib/base/enums.ts b/packages/storage/lib/base/enums.ts new file mode 100644 index 0000000..4f76282 --- /dev/null +++ b/packages/storage/lib/base/enums.ts @@ -0,0 +1,42 @@ +/** + * Storage area type for persisting and exchanging data. + * @see https://developer.chrome.com/docs/extensions/reference/storage/#overview + */ +export enum StorageEnum { + /** + * Persist data locally against browser restarts. Will be deleted by uninstalling the extension. + * @default + */ + Local = 'local', + /** + * Uploads data to the users account in the cloud and syncs to the users browsers on other devices. Limits apply. + */ + Sync = 'sync', + /** + * Requires an [enterprise policy](https://www.chromium.org/administrators/configuring-policy-for-extensions) with a + * json schema for company wide config. + */ + Managed = 'managed', + /** + * Only persist data until the browser is closed. Recommended for service workers which can shutdown anytime and + * therefore need to restore their state. Set {@link SessionAccessLevelEnum} for permitting content scripts access. + * @implements Chromes [Session Storage](https://developer.chrome.com/docs/extensions/reference/storage/#property-session) + */ + Session = 'session', +} + +/** + * Global access level requirement for the {@link StorageEnum.Session} Storage Area. + * @implements Chromes [Session Access Level](https://developer.chrome.com/docs/extensions/reference/storage/#method-StorageArea-setAccessLevel) + */ +export enum SessionAccessLevelEnum { + /** + * Storage can only be accessed by Extension pages (not Content scripts). + * @default + */ + ExtensionPagesOnly = 'TRUSTED_CONTEXTS', + /** + * Storage can be accessed by both Extension pages and Content scripts. + */ + ExtensionPagesAndContentScripts = 'TRUSTED_AND_UNTRUSTED_CONTEXTS', +} diff --git a/packages/storage/lib/base/index.ts b/packages/storage/lib/base/index.ts new file mode 100644 index 0000000..99d1fa7 --- /dev/null +++ b/packages/storage/lib/base/index.ts @@ -0,0 +1,3 @@ +export * from './base.js'; +export * from './enums.js'; +export type * from './types.js'; diff --git a/packages/storage/lib/base/types.ts b/packages/storage/lib/base/types.ts new file mode 100644 index 0000000..3f8e4b9 --- /dev/null +++ b/packages/storage/lib/base/types.ts @@ -0,0 +1,54 @@ +import type { StorageEnum } from './index.js'; + +export type ValueOrUpdateType = D | ((prev: D) => Promise | D); + +export type BaseStorageType = { + get: () => Promise; + set: (value: ValueOrUpdateType) => Promise; + getSnapshot: () => D | null; + subscribe: (listener: () => void) => () => void; +}; + +export type StorageConfigType = { + /** + * Assign the {@link StorageEnum} to use. + * @default Local + */ + storageEnum?: StorageEnum; + /** + * Only for {@link StorageEnum.Session}: Grant Content scripts access to storage area? + * @default false + */ + sessionAccessForContentScripts?: boolean; + /** + * Keeps state live in sync between all instances of the extension. Like between popup, side panel and content scripts. + * To allow chrome background scripts to stay in sync as well, use {@link StorageEnum.Session} storage area with + * {@link StorageConfigType.sessionAccessForContentScripts} potentially also set to true. + * @see https://stackoverflow.com/a/75637138/2763239 + * @default false + */ + liveUpdate?: boolean; + /** + * An optional props for converting values from storage and into it. + * @default undefined + */ + serialization?: { + /** + * convert non-native values to string to be saved in storage + */ + serialize: (value: D) => string; + /** + * convert string value from storage to non-native values + */ + deserialize: (text: string) => D; + }; +}; + +export interface ThemeStateType { + theme: 'light' | 'dark'; + isLight: boolean; +} + +export type ThemeStorageType = BaseStorageType & { + toggle: () => Promise; +}; diff --git a/packages/storage/lib/impl/example-theme-storage.ts b/packages/storage/lib/impl/example-theme-storage.ts new file mode 100644 index 0000000..2f74bbc --- /dev/null +++ b/packages/storage/lib/impl/example-theme-storage.ts @@ -0,0 +1,28 @@ +import { createStorage, StorageEnum } from '../base/index.js'; +import type { ThemeStateType, ThemeStorageType } from '../base/index.js'; + +const storage = createStorage( + 'theme-storage-key', + { + theme: 'light', + isLight: true, + }, + { + storageEnum: StorageEnum.Local, + liveUpdate: true, + }, +); + +export const exampleThemeStorage: ThemeStorageType = { + ...storage, + toggle: async () => { + await storage.set(currentState => { + const newTheme = currentState.theme === 'light' ? 'dark' : 'light'; + + return { + theme: newTheme, + isLight: newTheme === 'light', + }; + }); + }, +}; diff --git a/packages/storage/lib/impl/index.ts b/packages/storage/lib/impl/index.ts new file mode 100644 index 0000000..b09e484 --- /dev/null +++ b/packages/storage/lib/impl/index.ts @@ -0,0 +1 @@ +export * from './example-theme-storage.js'; diff --git a/packages/storage/lib/index.ts b/packages/storage/lib/index.ts new file mode 100644 index 0000000..4101c53 --- /dev/null +++ b/packages/storage/lib/index.ts @@ -0,0 +1,2 @@ +export type * from './types.js'; +export * from './impl/index.js'; diff --git a/packages/storage/lib/types.ts b/packages/storage/lib/types.ts new file mode 100644 index 0000000..6029353 --- /dev/null +++ b/packages/storage/lib/types.ts @@ -0,0 +1,8 @@ +import type { ValueOrUpdateType } from './base/index.js'; + +export type BaseStorageType = { + get: () => Promise; + set: (value: ValueOrUpdateType) => Promise; + getSnapshot: () => D | null; + subscribe: (listener: () => void) => () => void; +}; diff --git a/packages/storage/package.json b/packages/storage/package.json new file mode 100644 index 0000000..baaee6d --- /dev/null +++ b/packages/storage/package.json @@ -0,0 +1,27 @@ +{ + "name": "@extension/storage", + "version": "0.5.0", + "description": "chrome extension - storage", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*" + } +} diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/storage/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json new file mode 100644 index 0000000..7f86034 --- /dev/null +++ b/packages/tailwindcss-config/package.json @@ -0,0 +1,11 @@ +{ + "name": "@extension/tailwindcss-config", + "version": "0.5.0", + "description": "chrome extension - tailwindcss configuration", + "main": "tailwind.config.ts", + "private": true, + "sideEffects": false, + "devDependencies": { + "@extension/tsconfig": "workspace:*" + } +} diff --git a/packages/tailwindcss-config/tailwind.config.ts b/packages/tailwindcss-config/tailwind.config.ts new file mode 100644 index 0000000..8655879 --- /dev/null +++ b/packages/tailwindcss-config/tailwind.config.ts @@ -0,0 +1,8 @@ +import type { Config } from 'tailwindcss'; + +export default { + theme: { + extend: {}, + }, + plugins: [], +} as Omit; diff --git a/packages/tailwindcss-config/tsconfig.json b/packages/tailwindcss-config/tsconfig.json new file mode 100644 index 0000000..3469054 --- /dev/null +++ b/packages/tailwindcss-config/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": "." + }, + "include": ["tailwind.config.ts"] +} diff --git a/packages/tsconfig/app.json b/packages/tsconfig/app.json new file mode 100644 index 0000000..aa9d24d --- /dev/null +++ b/packages/tsconfig/app.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Chrome Extension App", + "extends": "./base.json" +} diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json new file mode 100644 index 0000000..ca39281 --- /dev/null +++ b/packages/tsconfig/base.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Base", + "compilerOptions": { + "allowJs": true, + "noEmit": true, + "downlevelIteration": true, + "isolatedModules": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "noImplicitReturns": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "bundler", + "target": "ESNext", + "lib": ["DOM", "ESNext"], + "types": ["node", "chrome"] + } +} diff --git a/packages/tsconfig/module.json b/packages/tsconfig/module.json new file mode 100644 index 0000000..045c028 --- /dev/null +++ b/packages/tsconfig/module.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Module", + "extends": "./base.json", + "compilerOptions": { + "noEmit": false, + "moduleResolution": "NodeNext", + "module": "NodeNext" + } +} diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 0000000..4fca940 --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,7 @@ +{ + "name": "@extension/tsconfig", + "version": "0.5.0", + "description": "chrome extension - tsconfig", + "private": true, + "sideEffects": false +} diff --git a/packages/ui/README.md b/packages/ui/README.md new file mode 100644 index 0000000..bd3d74d --- /dev/null +++ b/packages/ui/README.md @@ -0,0 +1,336 @@ +# UI Package + +This package provides components that make up the UI. + +## Installation + +First, move to the page you want to use. + +```shell +cd pages/options +``` + +Add the following to the dependencies in `package.json`. + +```json +{ + "dependencies": { + "@extension/ui": "workspace:*" + } +} +``` + +Then, run: + +```shell +pnpm install +``` + +Add the following to the `tailwind.config.ts` file. + +```ts +import baseConfig from '@extension/tailwindcss-config'; +import { withUI } from '@extension/ui'; + +export default withUI({ + ...baseConfig, + content: ['./index.html', './src/**/*.tsx'], +}); +``` + +Add the following to the first line of `index.css` file. + +```css +@import '@extension/ui/global.css'; +``` + +## Add Custom Component + +Add the following to the `lib/components/index.ts` file. + +```tsx +export * from './CustomComponent.js'; +``` + +Add the following to the `lib/components/CustomComponent.tsx` file. + +```tsx +import { cn } from '@/lib/utils.js'; +import type { ComponentPropsWithoutRef } from 'react'; + +type CustomComponentProps = ComponentPropsWithoutRef<'section'>; + +export const CustomComponent = ({ children, ...props }: CustomComponentProps) => { + return
{children}
; +} +``` + +## Usage + +```tsx +import { CustomComponent, ErrorDisplay, LoadingSpinner } from '@extension/ui'; + +const Page = () => { + return Hi, I'm a custom component.; +} + +export default withErrorBoundary(withSuspense(Page, ), ErrorDisplay); + +``` + +> [!TIP] +> You are able to set other size of the loading spinner by passing the `size` prop to the ``. + +## Modifying the tailwind config of the UI library + +Modify the `tailwind.config.ts` file to make global style changes to the package. + +## Modifying the css variable of the UI library + +Modify the css variable in the `ui/lib/global.css` code to change the css variable of all pages(with UI). + +## Guide for Adding shadcn to the UI Package + +You can refer to the this [manual guide](https://ui.shadcn.com/docs/installation/manual) + +1. Create components.json in packages/ui + + Create a file named `components.json` in the `packages/ui` directory with the following content: + + ```json + { + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "lib/global.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/lib/components", + "utils": "@/lib/utils", + "ui": "@/lib/components/ui", + "lib": "@/lib", + "hooks": "@/lib/hooks" + }, + "iconLibrary": "lucide" + } + ``` + +2. Install dependencies + + Run the following command from the root of your project: + + ```shell + pnpm add tailwindcss-animate class-variance-authority tailwind-merge lucide-react -F ui + ``` + +3. Edit `with-ui.ts` in `lib` folder + + This configuration file is from the manual guide. You can refer to the manual guide to modify the configuration file. ([ + `Configure tailwind.config.js`](https://ui.shadcn.com/docs/installation/manual)) + + ```ts + import deepmerge from 'deepmerge'; + import { fontFamily } from 'tailwindcss/defaultTheme'; + import tailwindAnimate from 'tailwindcss-animate'; + import type { Config } from 'tailwindcss'; + + export function withUI(tailwindConfig: Config): Config { + return deepmerge( + shadcnConfig, + deepmerge(tailwindConfig, { + content: ['./node_modules/@extension/ui/lib/**/*.{tsx,ts,js,jsx}'], + }), + ); + } + + const shadcnConfig = { + darkMode: ['class'], + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: 'calc(var(--radius) - 4px)', + }, + fontFamily: { + sans: ['var(--font-sans)', ...fontFamily.sans], + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, + }, + plugins: [tailwindAnimate], + }; + ``` + +4. Edit `global.css` in `lib` folder + + This configuration also comes from the manual guide. You can refer to the manual guide to modify the configuration + file. ([`Configure styles`](https://ui.shadcn.com/docs/installation/manual)) + + ```css + @tailwind base; + @tailwind components; + @tailwind utilities; + + @layer base { + :host, :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + --ring: 215 20.2% 65.1%; + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + --border: 216 34% 17%; + --input: 216 34% 17%; + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + --ring: 216 34% 17%; + } + } + + @layer base { + * { + @apply border-border; + } + + body { + @apply font-sans antialiased bg-background text-foreground; + } + } + ``` + + +5. Add shadcn components + + Finally, run this command from the root of your project to add the button component: + + ```shell + pnpm dlx shadcn@latest add button -c ./packages/ui + ``` + + This will add the shadcn button component to your UI package. + + Remember to adjust any paths or package names if your project structure differs from the assumed layout in this guide. + +6. Export components + +Edit the `index.ts` file in the `packages/ui` directory to export the shadcn ui component: + +```ts +//... +export * from './lib/components/ui/button'; +``` + +7. Apply global.css + +If you want to use shadcn components in content-ui ShadowDOM, you need to import ui package's global.css in the content-ui `tailwind-input.css` + +```css +@import '@extension/ui/lib/global.css'; + +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +If you want to use shadcn components in other pages, you need to import ui package's global.css in the `src/index.css` + +```css +@import '@extension/ui/lib/global.css'; + +@tailwind base; +@tailwind components; +@tailwind utilities; +``` diff --git a/packages/ui/global.css b/packages/ui/global.css new file mode 100644 index 0000000..3e8c163 --- /dev/null +++ b/packages/ui/global.css @@ -0,0 +1,20 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + margin: 0; + padding: 0; + box-sizing: border-box; + + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} diff --git a/packages/ui/index.ts b/packages/ui/index.ts new file mode 100644 index 0000000..11aece6 --- /dev/null +++ b/packages/ui/index.ts @@ -0,0 +1 @@ +export * from './lib/index'; diff --git a/packages/ui/lib/assets/warning.svg b/packages/ui/lib/assets/warning.svg new file mode 100644 index 0000000..6f561a3 --- /dev/null +++ b/packages/ui/lib/assets/warning.svg @@ -0,0 +1,10 @@ + + + diff --git a/packages/ui/lib/components/LoadingSpinner.tsx b/packages/ui/lib/components/LoadingSpinner.tsx new file mode 100644 index 0000000..333575c --- /dev/null +++ b/packages/ui/lib/components/LoadingSpinner.tsx @@ -0,0 +1,11 @@ +import { RingLoader } from 'react-spinners'; + +interface ILoadingSpinnerProps { + size?: number; +} + +export const LoadingSpinner = ({ size }: ILoadingSpinnerProps) => ( +
+ +
+); diff --git a/packages/ui/lib/components/ToggleButton.tsx b/packages/ui/lib/components/ToggleButton.tsx new file mode 100644 index 0000000..1bbc4e4 --- /dev/null +++ b/packages/ui/lib/components/ToggleButton.tsx @@ -0,0 +1,23 @@ +import { cn } from '@/lib/utils'; +import { useStorage } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import type { ComponentPropsWithoutRef } from 'react'; + +type ToggleButtonProps = ComponentPropsWithoutRef<'button'>; + +export const ToggleButton = ({ className, children, ...props }: ToggleButtonProps) => { + const { isLight } = useStorage(exampleThemeStorage); + + return ( + + ); +}; diff --git a/packages/ui/lib/components/error-display/ErrorDisplay.tsx b/packages/ui/lib/components/error-display/ErrorDisplay.tsx new file mode 100644 index 0000000..48ba66a --- /dev/null +++ b/packages/ui/lib/components/error-display/ErrorDisplay.tsx @@ -0,0 +1,13 @@ +import { ErrorHeader } from '@/lib/components/error-display/ErrorHeader'; +import { ErrorResetButton } from '@/lib/components/error-display/ErrorResetButton'; +import { ErrorStackTraceList } from '@/lib/components/error-display/ErrorStackTraceList'; + +export const ErrorDisplay = ({ error, resetErrorBoundary }: { error?: Error; resetErrorBoundary?: () => void }) => ( +
+
+ + + +
+
+); diff --git a/packages/ui/lib/components/error-display/ErrorHeader.tsx b/packages/ui/lib/components/error-display/ErrorHeader.tsx new file mode 100644 index 0000000..bfc820d --- /dev/null +++ b/packages/ui/lib/components/error-display/ErrorHeader.tsx @@ -0,0 +1,21 @@ +import { t } from '@extension/i18n'; + +// FIXME: IMPORT SVG ICON INSTEAD OF DEFINING INLINE IT HERE +const WarningIcon = ({ className }: { className: string }) => ( + + + +); + +export const ErrorHeader = () => ( +
+ +

{t('displayErrorInfo')}

+

{t('displayErrorDescription')}.

+
+); diff --git a/packages/ui/lib/components/error-display/ErrorResetButton.tsx b/packages/ui/lib/components/error-display/ErrorResetButton.tsx new file mode 100644 index 0000000..aa3ef85 --- /dev/null +++ b/packages/ui/lib/components/error-display/ErrorResetButton.tsx @@ -0,0 +1,11 @@ +import { t } from '@extension/i18n'; + +export const ErrorResetButton = ({ resetErrorBoundary }: { resetErrorBoundary?: () => void }) => ( +
+ +
+); diff --git a/packages/ui/lib/components/error-display/ErrorStackTraceList.tsx b/packages/ui/lib/components/error-display/ErrorStackTraceList.tsx new file mode 100644 index 0000000..6e28815 --- /dev/null +++ b/packages/ui/lib/components/error-display/ErrorStackTraceList.tsx @@ -0,0 +1,20 @@ +import { t } from '@extension/i18n'; + +export const ErrorStackTraceList = ({ error }: { error?: Error }) => ( +
+
+
+

{t('displayErrorDetailsInfo')}

+
+

{error?.message || t('displayErrorUnknownErrorInfo')}

+ {error?.stack && ( +
+ Stack trace +
{error?.stack}
+
+ )} +
+
+
+
+); diff --git a/packages/ui/lib/components/index.ts b/packages/ui/lib/components/index.ts new file mode 100644 index 0000000..b9b5348 --- /dev/null +++ b/packages/ui/lib/components/index.ts @@ -0,0 +1,3 @@ +export * from './ToggleButton'; +export * from './LoadingSpinner'; +export * from './error-display/ErrorDisplay'; diff --git a/packages/ui/lib/index.ts b/packages/ui/lib/index.ts new file mode 100644 index 0000000..1429201 --- /dev/null +++ b/packages/ui/lib/index.ts @@ -0,0 +1,3 @@ +export * from './components/index'; +export * from './utils'; +export * from './with-ui'; diff --git a/packages/ui/lib/utils.ts b/packages/ui/lib/utils.ts new file mode 100644 index 0000000..cfdd7d2 --- /dev/null +++ b/packages/ui/lib/utils.ts @@ -0,0 +1,5 @@ +import { clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; +import type { ClassValue } from 'clsx'; + +export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs)); diff --git a/packages/ui/lib/with-ui.ts b/packages/ui/lib/with-ui.ts new file mode 100644 index 0000000..5922ef0 --- /dev/null +++ b/packages/ui/lib/with-ui.ts @@ -0,0 +1,7 @@ +import deepmerge from 'deepmerge'; +import type { Config } from 'tailwindcss'; + +export const withUI = (tailwindConfig: Config): Config => + deepmerge(tailwindConfig, { + content: ['../../packages/ui/lib/**/*.tsx'], + }); diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..137121f --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,37 @@ +{ + "name": "@extension/ui", + "version": "0.5.0", + "description": "chrome extension - ui components", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "types": "index.ts", + "main": "dist/index.js", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "ready": "tsc -b && tsc-alias -p tsconfig.json", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/i18n": "workspace:*", + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "clsx": "^2.1.1", + "tailwind-merge": "^3.3.0" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "react-spinners": "^0.17.0", + "tsc-alias": "^1.8.16" + } +} diff --git a/packages/ui/tailwind.config.ts b/packages/ui/tailwind.config.ts new file mode 100644 index 0000000..c930fcd --- /dev/null +++ b/packages/ui/tailwind.config.ts @@ -0,0 +1,7 @@ +import globalConfig from '@extension/tailwindcss-config'; +import type { Config } from 'tailwindcss'; + +export default { + content: ['lib/**/*.tsx'], + presets: [globalConfig], +} satisfies Config; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000..f4cfc35 --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "outDir": "dist", + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + }, + "noEmit": false, + "declaration": true + }, + "include": ["index.ts", "lib", "tailwind.config.ts"] +} diff --git a/packages/vite-config/index.mts b/packages/vite-config/index.mts new file mode 100644 index 0000000..09c185e --- /dev/null +++ b/packages/vite-config/index.mts @@ -0,0 +1 @@ +export * from './lib/index.js'; diff --git a/packages/vite-config/lib/build-content-script.ts b/packages/vite-config/lib/build-content-script.ts new file mode 100644 index 0000000..09fe1f0 --- /dev/null +++ b/packages/vite-config/lib/build-content-script.ts @@ -0,0 +1,96 @@ +import { withPageConfig } from './index.js'; +import { IS_DEV } from '@extension/env'; +import { makeEntryPointPlugin } from '@extension/hmr'; +import { build as buildTW } from 'tailwindcss/lib/cli/build'; +import { build } from 'vite'; +import { readdirSync, statSync } from 'node:fs'; +import { resolve } from 'node:path'; + +interface IContentBuilderProps { + matchesDir: string; + srcDir: string; + rootDir: string; + contentName: 'content' | 'content-ui' | 'content-runtime'; + withTw: boolean; +} + +type BuilderPropsType = Omit; + +const getContentScriptEntries = (matchesDir: string) => { + const entryPoints: Record = {}; + const entries = readdirSync(matchesDir); + + entries.forEach((folder: string) => { + const filePath = resolve(matchesDir, folder); + const isFolder = statSync(filePath).isDirectory(); + const haveIndexTsFile = readdirSync(filePath).includes('config.ts'); + const haveIndexTsxFile = readdirSync(filePath).includes('index.tsx'); + + if (isFolder && !(haveIndexTsFile || haveIndexTsxFile)) { + throw new Error(`${folder} in \`matches\` doesn't have index.ts or index.tsx file`); + } else { + entryPoints[folder] = resolve(filePath, haveIndexTsFile ? 'config.ts' : 'index.tsx'); + } + }); + + return entryPoints; +}; + +const configsBuilder = ({ matchesDir, srcDir, rootDir, contentName }: BuilderPropsType) => + Object.entries(getContentScriptEntries(matchesDir)).map(([name, entry]) => ({ + name, + config: withPageConfig({ + mode: IS_DEV ? 'development' : undefined, + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + plugins: [IS_DEV && makeEntryPointPlugin()], + build: { + lib: { + name: name, + formats: ['iife'], + entry, + fileName: name, + }, + outDir: resolve(rootDir, '..', '..', 'dist', contentName), + }, + }), + })); + +const builds = async ({ srcDir, contentName, rootDir, matchesDir, withTw }: IContentBuilderProps) => + configsBuilder({ matchesDir, srcDir, rootDir, contentName }).map(async ({ name, config }) => { + if (withTw) { + const folder = resolve(matchesDir, name); + const args = { + ['--input']: resolve(folder, 'index.css'), + ['--output']: resolve(rootDir, 'dist', name, 'index.css'), + ['--config']: resolve(rootDir, 'tailwind.config.ts'), + ['--watch']: IS_DEV, + }; + + await buildTW(args); + } + + //@ts-expect-error This is hidden property from vite's resolveConfig() + config.configFile = false; + return build(config); + }); + +// FIXME: USE THIS FOR ALL CONTENT SCRIPTs +export const contentBuilder = async ({ + matchesDir, + srcDir, + rootDir, + contentName, + withTw = true, +}: IContentBuilderProps) => + builds({ + srcDir, + contentName, + rootDir, + matchesDir, + withTw, + }); diff --git a/packages/vite-config/lib/get-content-script-entires.ts b/packages/vite-config/lib/get-content-script-entires.ts new file mode 100644 index 0000000..c481cb4 --- /dev/null +++ b/packages/vite-config/lib/get-content-script-entires.ts @@ -0,0 +1,22 @@ +import { readdirSync, statSync } from 'node:fs'; +import { resolve } from 'node:path'; + +export const getContentScriptEntries = (matchesDir: string) => { + const entryPoints: Record = {}; + const entries = readdirSync(matchesDir); + + entries.forEach((folder: string) => { + const filePath = resolve(matchesDir, folder); + const isFolder = statSync(filePath).isDirectory(); + const haveIndexTsFile = readdirSync(filePath).includes('index.ts'); + const haveIndexTsxFile = readdirSync(filePath).includes('index.tsx'); + + if (isFolder && !(haveIndexTsFile || haveIndexTsxFile)) { + throw new Error(`${folder} in \`matches\` doesn't have index.ts or index.tsx file`); + } else { + entryPoints[folder] = resolve(filePath, haveIndexTsFile ? 'index.ts' : 'index.tsx'); + } + }); + + return entryPoints; +}; diff --git a/packages/vite-config/lib/index.ts b/packages/vite-config/lib/index.ts new file mode 100644 index 0000000..af40685 --- /dev/null +++ b/packages/vite-config/lib/index.ts @@ -0,0 +1,2 @@ +export * from './with-page-config.js'; +export * from './get-content-script-entires.js'; diff --git a/packages/vite-config/lib/with-page-config.ts b/packages/vite-config/lib/with-page-config.ts new file mode 100644 index 0000000..a0187fa --- /dev/null +++ b/packages/vite-config/lib/with-page-config.ts @@ -0,0 +1,39 @@ +import env, { IS_DEV, IS_PROD } from '@extension/env'; +import { watchRebuildPlugin } from '@extension/hmr'; +import react from '@vitejs/plugin-react-swc'; +import deepmerge from 'deepmerge'; +import { defineConfig } from 'vite'; +import { nodePolyfills } from 'vite-plugin-node-polyfills'; +import type { UserConfig } from 'vite'; + +export const watchOption = IS_DEV + ? { + chokidar: { + awaitWriteFinish: true, + }, + } + : undefined; + +export const withPageConfig = (config: UserConfig) => + defineConfig( + deepmerge( + { + define: { + 'process.env': env, + }, + base: '', + plugins: [react(), IS_DEV && watchRebuildPlugin({ refresh: true }), nodePolyfills()], + build: { + sourcemap: IS_DEV, + minify: IS_PROD, + reportCompressedSize: IS_PROD, + emptyOutDir: IS_PROD, + watch: watchOption, + rollupOptions: { + external: ['chrome'], + }, + }, + }, + config, + ), + ); diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json new file mode 100644 index 0000000..f12551d --- /dev/null +++ b/packages/vite-config/package.json @@ -0,0 +1,25 @@ +{ + "name": "@extension/vite-config", + "version": "0.5.0", + "description": "chrome extension - vite base configuration", + "type": "module", + "private": true, + "sideEffects": true, + "types": "index.mts", + "main": "dist/index.mjs", + "module": "dist/index.mjs", + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:bundle": "pnpx rimraf dist", + "clean": "pnpm clean:bundle && pnpm clean:node_modules", + "ready": "tsc -b" + }, + "dependencies": { + "@extension/env": "workspace:*" + }, + "devDependencies": { + "@extension/hmr": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@vitejs/plugin-react-swc": "^3.9.0" + } +} diff --git a/packages/vite-config/tailwind.d.ts b/packages/vite-config/tailwind.d.ts new file mode 100644 index 0000000..6cf861a --- /dev/null +++ b/packages/vite-config/tailwind.d.ts @@ -0,0 +1 @@ +declare module 'tailwindcss/lib/cli/build'; diff --git a/packages/vite-config/tsconfig.json b/packages/vite-config/tsconfig.json new file mode 100644 index 0000000..5e3961e --- /dev/null +++ b/packages/vite-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["lib", "index.mts", "tailwind.d.ts"] +} diff --git a/packages/zipper/index.mts b/packages/zipper/index.mts new file mode 100644 index 0000000..7d0531f --- /dev/null +++ b/packages/zipper/index.mts @@ -0,0 +1,13 @@ +import { resolve } from 'node:path'; +import { zipBundle } from './lib/index.js'; +import { IS_FIREFOX } from '@extension/env'; + +const YYYY_MM_DD = new Date().toISOString().slice(0, 10).replace(/-/g, ''); +const HH_mm_ss = new Date().toISOString().slice(11, 19).replace(/:/g, ''); +const fileName = `extension-${YYYY_MM_DD}-${HH_mm_ss}`; + +await zipBundle({ + distDirectory: resolve(import.meta.dirname, '..', '..', '..', 'dist'), + buildDirectory: resolve(import.meta.dirname, '..', '..', '..', 'dist-zip'), + archiveName: IS_FIREFOX ? `${fileName}.xpi` : `${fileName}.zip`, +}); diff --git a/packages/zipper/lib/index.ts b/packages/zipper/lib/index.ts new file mode 100644 index 0000000..8d19cde --- /dev/null +++ b/packages/zipper/lib/index.ts @@ -0,0 +1 @@ +export * from './zip-bundle.js'; diff --git a/packages/zipper/lib/zip-bundle.ts b/packages/zipper/lib/zip-bundle.ts new file mode 100644 index 0000000..e5704a7 --- /dev/null +++ b/packages/zipper/lib/zip-bundle.ts @@ -0,0 +1,87 @@ +import { streamFileToZip } from '@extension/dev-utils'; +import fg from 'fast-glob'; +import { Zip } from 'fflate'; +import { createWriteStream, existsSync, mkdirSync } from 'node:fs'; +import { posix, resolve } from 'node:path'; + +const toMB = (bytes: number): number => bytes / 1024 / 1024; + +const ensureBuildDirectoryExists = (buildDirectory: string): void => { + if (!existsSync(buildDirectory)) { + mkdirSync(buildDirectory, { recursive: true }); + } +}; + +const logPackageSize = (size: number, startTime: number): void => { + console.log(`Zip Package size: ${toMB(size).toFixed(2)} MB in ${Date.now() - startTime}ms`); +}; + +export const zipBundle = async ( + { + distDirectory, + buildDirectory, + archiveName, + }: { + distDirectory: string; + buildDirectory: string; + archiveName: string; + }, + withMaps = false, +): Promise => { + ensureBuildDirectoryExists(buildDirectory); + + const zipFilePath = resolve(buildDirectory, archiveName); + const output = createWriteStream(zipFilePath); + + const fileList = await fg( + [ + '**/*', // Pick all nested files + ...(!withMaps ? ['!**/(*.js.map|*.css.map)'] : []), // Exclude source maps conditionally + ], + { + cwd: distDirectory, + onlyFiles: true, + }, + ); + + return new Promise((pResolve, pReject) => { + let aborted = false; + let totalSize = 0; + const timer = Date.now(); + const zip = new Zip((err, data, final) => { + if (err) { + pReject(err); + } else { + totalSize += data.length; + output.write(data); + if (final) { + logPackageSize(totalSize, timer); + output.end(); + pResolve(); + } + } + }); + + for (const file of fileList) { + if (aborted) return; + + const absPath = resolve(distDirectory, file); + const absPosixPath = posix.resolve(distDirectory, file); + const relPosixPath = posix.relative(distDirectory, absPosixPath); + + console.log(`Adding file: ${relPosixPath}`); + streamFileToZip( + absPath, + relPosixPath, + zip, + () => { + aborted = true; + zip.terminate(); + }, + error => pReject(`Error reading file ${absPath}: ${error.message}`), + ); + } + + zip.end(); + }); +}; diff --git a/packages/zipper/package.json b/packages/zipper/package.json new file mode 100644 index 0000000..b230e03 --- /dev/null +++ b/packages/zipper/package.json @@ -0,0 +1,30 @@ +{ + "name": "@extension/zipper", + "version": "0.5.0", + "description": "chrome extension - zipper", + "type": "module", + "private": true, + "sideEffects": false, + "files": [ + "dist/**" + ], + "types": "index.mts", + "main": "dist/index.mjs", + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "zip": "node --env-file=../../.env dist/index.mjs", + "lint": "eslint .", + "ready": "tsc -b", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*", + "@extension/dev-utils": "workspace:*", + "@extension/env": "workspace:*" + } +} diff --git a/packages/zipper/tsconfig.json b/packages/zipper/tsconfig.json new file mode 100644 index 0000000..40168e1 --- /dev/null +++ b/packages/zipper/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": ["index.mts", "lib"] +} diff --git a/pages/content-runtime/README.md b/pages/content-runtime/README.md new file mode 100644 index 0000000..14f699e --- /dev/null +++ b/pages/content-runtime/README.md @@ -0,0 +1,15 @@ +# Content Runtime Script + +This tool allows users to inject scripts (Console and UI) during runtime into all pages specified by you. + +### Add New Script + +1. Copy `matches/example` folder and paste it with other name and edit content. +2. Define somewhere(e.g in `popup`(You have declared it as default)): + +```ts +await chrome.scripting.executeScript({ + ..., + files: ['/content-runtime/{matches_folder_name}.iife.js'], +}) +``` diff --git a/pages/content-runtime/build.mts b/pages/content-runtime/build.mts new file mode 100644 index 0000000..7c6f238 --- /dev/null +++ b/pages/content-runtime/build.mts @@ -0,0 +1,49 @@ +import { resolve } from 'node:path'; +import { makeEntryPointPlugin } from '@extension/hmr'; +import { getContentScriptEntries, withPageConfig } from '@extension/vite-config'; +import { IS_DEV } from '@extension/env'; +import { build } from 'vite'; +import { build as buildTW } from 'tailwindcss/lib/cli/build'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); +const matchesDir = resolve(srcDir, 'matches'); + +const configs = Object.entries(getContentScriptEntries(matchesDir)).map(([name, entry]) => ({ + name, + config: withPageConfig({ + mode: IS_DEV ? 'development' : undefined, + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + plugins: [IS_DEV && makeEntryPointPlugin()], + build: { + lib: { + name: name, + formats: ['iife'], + entry, + fileName: name, + }, + outDir: resolve(rootDir, '..', '..', 'dist', 'content-runtime'), + }, + }), +})); + +const builds = configs.map(async ({ name, config }) => { + const folder = resolve(matchesDir, name); + const args = { + ['--input']: resolve(folder, 'index.css'), + ['--output']: resolve(rootDir, 'dist', name, 'index.css'), + ['--config']: resolve(rootDir, 'tailwind.config.ts'), + ['--watch']: IS_DEV, + }; + await buildTW(args); + //@ts-expect-error This is hidden property into vite's resolveConfig() + config.configFile = false; + await build(config); +}); + +await Promise.all(builds); diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json new file mode 100644 index 0000000..60f302e --- /dev/null +++ b/pages/content-runtime/package.json @@ -0,0 +1,32 @@ +{ + "name": "@extension/content-runtime-script", + "version": "0.5.0", + "description": "chrome extension - content runtime script", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "tsx build.mts", + "dev": "tsx build.mts", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/env": "workspace:*", + "@extension/ui": "workspace:*" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*", + "@extension/hmr": "workspace:*", + "@extension/shared": "workspace:*" + } +} diff --git a/pages/content-runtime/src/matches/all/App.tsx b/pages/content-runtime/src/matches/all/App.tsx new file mode 100644 index 0000000..78c837b --- /dev/null +++ b/pages/content-runtime/src/matches/all/App.tsx @@ -0,0 +1,9 @@ +import { useEffect } from 'react'; + +export default function App() { + useEffect(() => { + console.log('[CEB] All runtime content view loaded'); + }, []); + + return
All runtime content view
; +} diff --git a/pages/content-runtime/src/matches/all/index.css b/pages/content-runtime/src/matches/all/index.css new file mode 100644 index 0000000..3f6a81e --- /dev/null +++ b/pages/content-runtime/src/matches/all/index.css @@ -0,0 +1,5 @@ +@import '@extension/ui/global.css'; + +.ceb-all-runtime-content-view-text { + font-size: 20px; +} diff --git a/pages/content-runtime/src/matches/all/index.tsx b/pages/content-runtime/src/matches/all/index.tsx new file mode 100644 index 0000000..817e373 --- /dev/null +++ b/pages/content-runtime/src/matches/all/index.tsx @@ -0,0 +1,5 @@ +import inlineCss from '../../../dist/all/index.css?inline'; +import { initAppWithShadow } from '@extension/shared'; +import App from '@src/matches/all/App'; + +initAppWithShadow({ id: 'CEB-extension-runtime-all', app: , inlineCss }); diff --git a/pages/content-runtime/src/matches/example/App.tsx b/pages/content-runtime/src/matches/example/App.tsx new file mode 100644 index 0000000..6d12e82 --- /dev/null +++ b/pages/content-runtime/src/matches/example/App.tsx @@ -0,0 +1,9 @@ +import { useEffect } from 'react'; + +export default function App() { + useEffect(() => { + console.log('[CEB] Example runtime content view loaded'); + }, []); + + return
Example runtime content view
; +} diff --git a/pages/content-runtime/src/matches/example/index.css b/pages/content-runtime/src/matches/example/index.css new file mode 100644 index 0000000..59c15eb --- /dev/null +++ b/pages/content-runtime/src/matches/example/index.css @@ -0,0 +1,5 @@ +@import '@extension/ui/global.css'; + +.ceb-example-runtime-content-view-text { + font-size: 20px; +} diff --git a/pages/content-runtime/src/matches/example/index.tsx b/pages/content-runtime/src/matches/example/index.tsx new file mode 100644 index 0000000..4b7efc7 --- /dev/null +++ b/pages/content-runtime/src/matches/example/index.tsx @@ -0,0 +1,5 @@ +import inlineCss from '../../../dist/example/index.css?inline'; +import { initAppWithShadow } from '@extension/shared'; +import App from '@src/matches/example/App'; + +initAppWithShadow({ id: 'CEB-extension-runtime-example', app: , inlineCss }); diff --git a/pages/content-runtime/tailwind.config.ts b/pages/content-runtime/tailwind.config.ts new file mode 100644 index 0000000..378f1b8 --- /dev/null +++ b/pages/content-runtime/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['src/**/*.tsx'], +}); diff --git a/pages/content-runtime/tailwind.d.ts b/pages/content-runtime/tailwind.d.ts new file mode 100644 index 0000000..021d8bf --- /dev/null +++ b/pages/content-runtime/tailwind.d.ts @@ -0,0 +1,5 @@ +declare module 'tailwindcss/lib/cli/build'; +declare module '*?inline' { + const src: string; + export default src; +} diff --git a/pages/content-runtime/tsconfig.json b/pages/content-runtime/tsconfig.json new file mode 100644 index 0000000..879e32c --- /dev/null +++ b/pages/content-runtime/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node", "./tailwind.d.ts"], + "allowImportingTsExtensions": true + }, + "include": ["src", "build.mts", "tailwind.config.ts"] +} diff --git a/pages/content-ui/README.md b/pages/content-ui/README.md new file mode 100644 index 0000000..47142c5 --- /dev/null +++ b/pages/content-ui/README.md @@ -0,0 +1,23 @@ +# Content UI Script + +This tool allows you to inject React Components into all pages specified by you. + +### Add New Script + +1. Copy `matches/example` folder and paste it with other name and edit content. + + > [!NOTE] + > Remember to edit import: + > ```ts + > import App from '@src/matches/{new_folder}/App'; + > ``` + +2. Edit `manifest.ts`: +- In `content-scripts` section add object with: + +```ts +{ + matches: ['URL_FOR_INJECT'], + js: ['content-ui/{matches_folder_name}.iife.js'] +} +``` \ No newline at end of file diff --git a/pages/content-ui/build.mts b/pages/content-ui/build.mts new file mode 100644 index 0000000..293bee5 --- /dev/null +++ b/pages/content-ui/build.mts @@ -0,0 +1,49 @@ +import { resolve } from 'node:path'; +import { makeEntryPointPlugin } from '@extension/hmr'; +import { getContentScriptEntries, withPageConfig } from '@extension/vite-config'; +import { IS_DEV } from '@extension/env'; +import { build } from 'vite'; +import { build as buildTW } from 'tailwindcss/lib/cli/build'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); +const matchesDir = resolve(srcDir, 'matches'); + +const configs = Object.entries(getContentScriptEntries(matchesDir)).map(([name, entry]) => ({ + name, + config: withPageConfig({ + mode: IS_DEV ? 'development' : undefined, + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + plugins: [IS_DEV && makeEntryPointPlugin()], + build: { + lib: { + name: name, + formats: ['iife'], + entry, + fileName: name, + }, + outDir: resolve(rootDir, '..', '..', 'dist', 'content-ui'), + }, + }), +})); + +const builds = configs.map(async ({ name, config }) => { + const folder = resolve(matchesDir, name); + const args = { + ['--input']: resolve(folder, 'index.css'), + ['--output']: resolve(rootDir, 'dist', name, 'index.css'), + ['--config']: resolve(rootDir, 'tailwind.config.ts'), + ['--watch']: IS_DEV, + }; + await buildTW(args); + //@ts-expect-error This is hidden property into vite's resolveConfig() + config.configFile = false; + await build(config); +}); + +await Promise.all(builds); diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json new file mode 100644 index 0000000..b20878d --- /dev/null +++ b/pages/content-ui/package.json @@ -0,0 +1,35 @@ +{ + "name": "@extension/content-ui", + "version": "0.5.0", + "description": "chrome extension - content ui", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:node_modules && pnpm clean:turbo", + "build": "tsx build.mts", + "dev": "tsx build.mts", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/ui": "workspace:*", + "@extension/i18n": "workspace:*", + "@extension/env": "workspace:*" + }, + "devDependencies": { + "@extension/hmr": "workspace:*", + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + } +} diff --git a/pages/content-ui/public/logo.svg b/pages/content-ui/public/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/pages/content-ui/public/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pages/content-ui/src/matches/all/App.tsx b/pages/content-ui/src/matches/all/App.tsx new file mode 100644 index 0000000..59dbd3c --- /dev/null +++ b/pages/content-ui/src/matches/all/App.tsx @@ -0,0 +1,18 @@ +import { t } from '@extension/i18n'; +import { ToggleButton } from '@extension/ui'; +import { useEffect } from 'react'; + +export default function App() { + useEffect(() => { + console.log('[CEB] Content ui all loaded'); + }, []); + + return ( +
+
+ Edit pages/content-ui/src/matches/all/App.tsx and save to reload. +
+ {t('toggleTheme')} +
+ ); +} diff --git a/pages/content-ui/src/matches/all/index.css b/pages/content-ui/src/matches/all/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/content-ui/src/matches/all/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/content-ui/src/matches/all/index.tsx b/pages/content-ui/src/matches/all/index.tsx new file mode 100644 index 0000000..4f55148 --- /dev/null +++ b/pages/content-ui/src/matches/all/index.tsx @@ -0,0 +1,5 @@ +import inlineCss from '../../../dist/all/index.css?inline'; +import { initAppWithShadow } from '@extension/shared'; +import App from '@src/matches/all/App'; + +initAppWithShadow({ id: 'CEB-extension-all', app: , inlineCss }); diff --git a/pages/content-ui/src/matches/example/App.tsx b/pages/content-ui/src/matches/example/App.tsx new file mode 100644 index 0000000..b3ef6ab --- /dev/null +++ b/pages/content-ui/src/matches/example/App.tsx @@ -0,0 +1,18 @@ +import { t } from '@extension/i18n'; +import { ToggleButton } from '@extension/ui'; +import { useEffect } from 'react'; + +export default function App() { + useEffect(() => { + console.log('[CEB] Content ui example loaded'); + }, []); + + return ( +
+
+ Edit pages/content-ui/src/matches/example/App.tsx and save to reload. +
+ {t('toggleTheme')} +
+ ); +} diff --git a/pages/content-ui/src/matches/example/index.css b/pages/content-ui/src/matches/example/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/content-ui/src/matches/example/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/content-ui/src/matches/example/index.tsx b/pages/content-ui/src/matches/example/index.tsx new file mode 100644 index 0000000..8c90986 --- /dev/null +++ b/pages/content-ui/src/matches/example/index.tsx @@ -0,0 +1,5 @@ +import inlineCss from '../../../dist/example/index.css?inline'; +import { initAppWithShadow } from '@extension/shared'; +import App from '@src/matches/example/App'; + +initAppWithShadow({ id: 'CEB-extension-example', app: , inlineCss }); diff --git a/pages/content-ui/tailwind.config.ts b/pages/content-ui/tailwind.config.ts new file mode 100644 index 0000000..378f1b8 --- /dev/null +++ b/pages/content-ui/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['src/**/*.tsx'], +}); diff --git a/pages/content-ui/tailwind.d.ts b/pages/content-ui/tailwind.d.ts new file mode 100644 index 0000000..021d8bf --- /dev/null +++ b/pages/content-ui/tailwind.d.ts @@ -0,0 +1,5 @@ +declare module 'tailwindcss/lib/cli/build'; +declare module '*?inline' { + const src: string; + export default src; +} diff --git a/pages/content-ui/tsconfig.json b/pages/content-ui/tsconfig.json new file mode 100644 index 0000000..aceb8d1 --- /dev/null +++ b/pages/content-ui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node", "./tailwind.d.ts"] + }, + "include": ["src", "tailwind.config.ts", "build.mts"] +} diff --git a/pages/content/README.md b/pages/content/README.md new file mode 100644 index 0000000..5553a2c --- /dev/null +++ b/pages/content/README.md @@ -0,0 +1,18 @@ +# Content Script + +This tool allows you to inject Console Scripts into all pages specified by you. + +https://developer.chrome.com/docs/extensions/develop/concepts/content-scripts + +### Add New Script + +1. Copy `matches/example` folder and paste it with other name and edit content. +2. Edit `manifest.ts`: +- In `content-scripts` section add object with: + +```ts +{ + matches: ['URL_FOR_INJECT'], + js: ['content/{matches_folder_name}.iife.js'] +} +``` \ No newline at end of file diff --git a/pages/content/build.mts b/pages/content/build.mts new file mode 100644 index 0000000..098fb51 --- /dev/null +++ b/pages/content/build.mts @@ -0,0 +1,39 @@ +import { resolve } from 'node:path'; +import { makeEntryPointPlugin } from '@extension/hmr'; +import { getContentScriptEntries, withPageConfig } from '@extension/vite-config'; +import { IS_DEV } from '@extension/env'; +import { build } from 'vite'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); +const matchesDir = resolve(srcDir, 'matches'); + +const configs = Object.entries(getContentScriptEntries(matchesDir)).map(([name, entry]) => + withPageConfig({ + mode: IS_DEV ? 'development' : undefined, + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + plugins: [IS_DEV && makeEntryPointPlugin()], + build: { + lib: { + name: name, + formats: ['iife'], + entry, + fileName: name, + }, + outDir: resolve(rootDir, '..', '..', 'dist', 'content'), + }, + }), +); + +const builds = configs.map(async config => { + //@ts-expect-error This is hidden property into vite's resolveConfig() + config.configFile = false; + await build(config); +}); + +await Promise.all(builds); diff --git a/pages/content/package.json b/pages/content/package.json new file mode 100644 index 0000000..2c36886 --- /dev/null +++ b/pages/content/package.json @@ -0,0 +1,33 @@ +{ + "name": "@extension/content-script", + "version": "0.5.0", + "description": "chrome extension - content script", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:bundle": "rimraf dist", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:bundle && pnpm clean:turbo && pnpm clean:node_modules", + "build": "tsx build.mts", + "dev": "tsx build.mts", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/env": "workspace:*" + }, + "devDependencies": { + "@extension/hmr": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + } +} diff --git a/pages/content/public/logo.svg b/pages/content/public/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/pages/content/public/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pages/content/src/matches/all/index.ts b/pages/content/src/matches/all/index.ts new file mode 100644 index 0000000..8374460 --- /dev/null +++ b/pages/content/src/matches/all/index.ts @@ -0,0 +1,5 @@ +import { sampleFunction } from '@src/sample-function'; + +console.log('[CEB] All content script loaded'); + +void sampleFunction(); diff --git a/pages/content/src/matches/example/index.ts b/pages/content/src/matches/example/index.ts new file mode 100644 index 0000000..de79d5d --- /dev/null +++ b/pages/content/src/matches/example/index.ts @@ -0,0 +1,5 @@ +import { sampleFunction } from '@src/sample-function'; + +console.log('[CEB] Example content script loaded'); + +void sampleFunction(); diff --git a/pages/content/src/sample-function.ts b/pages/content/src/sample-function.ts new file mode 100644 index 0000000..a0d72e8 --- /dev/null +++ b/pages/content/src/sample-function.ts @@ -0,0 +1,3 @@ +export const sampleFunction = () => { + console.log('content script - sampleFunction() called from another module'); +}; diff --git a/pages/content/tsconfig.json b/pages/content/tsconfig.json new file mode 100644 index 0000000..c8186a2 --- /dev/null +++ b/pages/content/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "build.mts"] +} diff --git a/pages/devtools-panel/index.html b/pages/devtools-panel/index.html new file mode 100644 index 0000000..8402fc1 --- /dev/null +++ b/pages/devtools-panel/index.html @@ -0,0 +1,12 @@ + + + + + Devtools Panel + + + +
+ + + diff --git a/pages/devtools-panel/package.json b/pages/devtools-panel/package.json new file mode 100644 index 0000000..332a9c3 --- /dev/null +++ b/pages/devtools-panel/package.json @@ -0,0 +1,39 @@ +{ + "name": "@extension/devtools-panel", + "version": "0.5.0", + "description": "chrome extension - devtools panel", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/i18n": "workspace:*", + "@extension/ui": "workspace:*" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } + } +} diff --git a/pages/devtools-panel/public/logo_horizontal.svg b/pages/devtools-panel/public/logo_horizontal.svg new file mode 100644 index 0000000..232e97b --- /dev/null +++ b/pages/devtools-panel/public/logo_horizontal.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/devtools-panel/public/logo_horizontal_dark.svg b/pages/devtools-panel/public/logo_horizontal_dark.svg new file mode 100644 index 0000000..cf19a3e --- /dev/null +++ b/pages/devtools-panel/public/logo_horizontal_dark.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/devtools-panel/src/Panel.css b/pages/devtools-panel/src/Panel.css new file mode 100644 index 0000000..7354dea --- /dev/null +++ b/pages/devtools-panel/src/Panel.css @@ -0,0 +1,28 @@ +.App { + text-align: center; + height: 100vh; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.App-logo { + height: 40vmin; +} + +.App-header { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} + +code { + background: rgba(148, 163, 184, 0.5); + border-radius: 0.25rem; + padding: 0.2rem 0.5rem; +} diff --git a/pages/devtools-panel/src/Panel.tsx b/pages/devtools-panel/src/Panel.tsx new file mode 100644 index 0000000..1f71c89 --- /dev/null +++ b/pages/devtools-panel/src/Panel.tsx @@ -0,0 +1,45 @@ +import '@src/Panel.css'; +import { t } from '@extension/i18n'; +import { PROJECT_URL_OBJECT, useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import { cn, ErrorDisplay, LoadingSpinner } from '@extension/ui'; +import type { ComponentPropsWithoutRef } from 'react'; + +const Panel = () => { + const { isLight } = useStorage(exampleThemeStorage); + const logo = isLight ? 'devtools-panel/logo_horizontal.svg' : 'devtools-panel/logo_horizontal_dark.svg'; + + const goGithubSite = () => chrome.tabs.create(PROJECT_URL_OBJECT); + + return ( +
+
+ +

+ Edit pages/devtools-panel/src/Panel.tsx +

+ {t('toggleTheme')} +
+
+ ); +}; + +const ToggleButton = (props: ComponentPropsWithoutRef<'button'>) => { + const { isLight } = useStorage(exampleThemeStorage); + + return ( + + ); +}; + +export default withErrorBoundary(withSuspense(Panel, ), ErrorDisplay); diff --git a/pages/devtools-panel/src/index.css b/pages/devtools-panel/src/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/devtools-panel/src/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/devtools-panel/src/index.tsx b/pages/devtools-panel/src/index.tsx new file mode 100644 index 0000000..b4e1ed5 --- /dev/null +++ b/pages/devtools-panel/src/index.tsx @@ -0,0 +1,15 @@ +import '@src/index.css'; +import Panel from '@src/Panel'; +import { createRoot } from 'react-dom/client'; + +const init = () => { + const appContainer = document.querySelector('#app-container'); + if (!appContainer) { + throw new Error('Can not find #app-container'); + } + const root = createRoot(appContainer); + + root.render(); +}; + +init(); diff --git a/pages/devtools-panel/tailwind.config.ts b/pages/devtools-panel/tailwind.config.ts new file mode 100644 index 0000000..3418ee6 --- /dev/null +++ b/pages/devtools-panel/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['index.html', 'src/**/*.tsx'], +}); diff --git a/pages/devtools-panel/tsconfig.json b/pages/devtools-panel/tsconfig.json new file mode 100644 index 0000000..b85f4eb --- /dev/null +++ b/pages/devtools-panel/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts", "tailwind.config.ts"] +} diff --git a/pages/devtools-panel/vite.config.mts b/pages/devtools-panel/vite.config.mts new file mode 100644 index 0000000..4eab241 --- /dev/null +++ b/pages/devtools-panel/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'devtools-panel'), + }, +}); diff --git a/pages/devtools/index.html b/pages/devtools/index.html new file mode 100644 index 0000000..80ebcea --- /dev/null +++ b/pages/devtools/index.html @@ -0,0 +1,11 @@ + + + + + Devtools + + + + + + diff --git a/pages/devtools/package.json b/pages/devtools/package.json new file mode 100644 index 0000000..f2e4d92 --- /dev/null +++ b/pages/devtools/package.json @@ -0,0 +1,29 @@ +{ + "name": "@extension/devtools", + "version": "0.5.0", + "description": "chrome extension - devtools", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*" + }, + "devDependencies": { + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + } +} diff --git a/pages/devtools/public/logo.svg b/pages/devtools/public/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/pages/devtools/public/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pages/devtools/src/index.ts b/pages/devtools/src/index.ts new file mode 100644 index 0000000..41fd50c --- /dev/null +++ b/pages/devtools/src/index.ts @@ -0,0 +1,6 @@ +try { + console.log("Edit 'pages/devtools/src/index.ts' and save to reload."); + chrome.devtools.panels.create('Dev Tools', '/icon-34.png', '/devtools-panel/index.html'); +} catch (e) { + console.error(e); +} diff --git a/pages/devtools/tsconfig.json b/pages/devtools/tsconfig.json new file mode 100644 index 0000000..226e61f --- /dev/null +++ b/pages/devtools/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts"] +} diff --git a/pages/devtools/vite.config.mts b/pages/devtools/vite.config.mts new file mode 100644 index 0000000..70b4ea8 --- /dev/null +++ b/pages/devtools/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'devtools'), + }, +}); diff --git a/pages/new-tab/index.html b/pages/new-tab/index.html new file mode 100644 index 0000000..121ef98 --- /dev/null +++ b/pages/new-tab/index.html @@ -0,0 +1,12 @@ + + + + + New Tab + + + +
+ + + diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json new file mode 100644 index 0000000..d33fd03 --- /dev/null +++ b/pages/new-tab/package.json @@ -0,0 +1,40 @@ +{ + "name": "@extension/new-tab", + "version": "0.5.0", + "description": "chrome extension - new tab", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/i18n": "workspace:*", + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/ui": "workspace:*" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*", + "sass": "^1.89.0" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } + } +} diff --git a/pages/new-tab/public/logo_horizontal.svg b/pages/new-tab/public/logo_horizontal.svg new file mode 100644 index 0000000..232e97b --- /dev/null +++ b/pages/new-tab/public/logo_horizontal.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/new-tab/public/logo_horizontal_dark.svg b/pages/new-tab/public/logo_horizontal_dark.svg new file mode 100644 index 0000000..cf19a3e --- /dev/null +++ b/pages/new-tab/public/logo_horizontal_dark.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/new-tab/src/NewTab.css b/pages/new-tab/src/NewTab.css new file mode 100644 index 0000000..26c291a --- /dev/null +++ b/pages/new-tab/src/NewTab.css @@ -0,0 +1,22 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; +} + +.App-header { + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} + +code { + background: rgba(148, 163, 184, 0.5); + border-radius: 0.25rem; + padding: 0.2rem 0.5rem; +} diff --git a/pages/new-tab/src/NewTab.scss b/pages/new-tab/src/NewTab.scss new file mode 100644 index 0000000..8960c7b --- /dev/null +++ b/pages/new-tab/src/NewTab.scss @@ -0,0 +1,10 @@ +$myColor: red; + +h1, +h2, +h3, +h4, +h5, +h6 { + color: $myColor; +} diff --git a/pages/new-tab/src/NewTab.tsx b/pages/new-tab/src/NewTab.tsx new file mode 100644 index 0000000..66eeb5b --- /dev/null +++ b/pages/new-tab/src/NewTab.tsx @@ -0,0 +1,31 @@ +import '@src/NewTab.css'; +import '@src/NewTab.scss'; +import { t } from '@extension/i18n'; +import { PROJECT_URL_OBJECT, useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; + +const NewTab = () => { + const { isLight } = useStorage(exampleThemeStorage); + const logo = isLight ? 'new-tab/logo_horizontal.svg' : 'new-tab/logo_horizontal_dark.svg'; + + const goGithubSite = () => chrome.tabs.create(PROJECT_URL_OBJECT); + + console.log(t('hello', 'World')); + return ( +
+
+ +

+ Edit pages/new-tab/src/NewTab.tsx +

+
The color of this paragraph is defined using SASS.
+ {t('toggleTheme')} +
+
+ ); +}; + +export default withErrorBoundary(withSuspense(NewTab, ), ErrorDisplay); diff --git a/pages/new-tab/src/index.css b/pages/new-tab/src/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/new-tab/src/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/new-tab/src/index.tsx b/pages/new-tab/src/index.tsx new file mode 100644 index 0000000..49b8150 --- /dev/null +++ b/pages/new-tab/src/index.tsx @@ -0,0 +1,15 @@ +import '@src/index.css'; +import NewTab from '@src/NewTab'; +import { createRoot } from 'react-dom/client'; + +const init = () => { + const appContainer = document.querySelector('#app-container'); + if (!appContainer) { + throw new Error('Can not find #app-container'); + } + const root = createRoot(appContainer); + + root.render(); +}; + +init(); diff --git a/pages/new-tab/tailwind.config.ts b/pages/new-tab/tailwind.config.ts new file mode 100644 index 0000000..3418ee6 --- /dev/null +++ b/pages/new-tab/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['index.html', 'src/**/*.tsx'], +}); diff --git a/pages/new-tab/tsconfig.json b/pages/new-tab/tsconfig.json new file mode 100644 index 0000000..b85f4eb --- /dev/null +++ b/pages/new-tab/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts", "tailwind.config.ts"] +} diff --git a/pages/new-tab/vite.config.mts b/pages/new-tab/vite.config.mts new file mode 100644 index 0000000..f473549 --- /dev/null +++ b/pages/new-tab/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'new-tab'), + }, +}); diff --git a/pages/options/index.html b/pages/options/index.html new file mode 100644 index 0000000..65e1fdd --- /dev/null +++ b/pages/options/index.html @@ -0,0 +1,12 @@ + + + + + Options + + + +
+ + + diff --git a/pages/options/package.json b/pages/options/package.json new file mode 100644 index 0000000..79ac3fd --- /dev/null +++ b/pages/options/package.json @@ -0,0 +1,39 @@ +{ + "name": "@extension/options", + "version": "0.5.0", + "description": "chrome extension - options", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/ui": "workspace:*", + "@extension/i18n": "workspace:*" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } + } +} diff --git a/pages/options/public/logo_horizontal.svg b/pages/options/public/logo_horizontal.svg new file mode 100644 index 0000000..232e97b --- /dev/null +++ b/pages/options/public/logo_horizontal.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/options/public/logo_horizontal_dark.svg b/pages/options/public/logo_horizontal_dark.svg new file mode 100644 index 0000000..cf19a3e --- /dev/null +++ b/pages/options/public/logo_horizontal_dark.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/options/src/Options.css b/pages/options/src/Options.css new file mode 100644 index 0000000..6e36d2a --- /dev/null +++ b/pages/options/src/Options.css @@ -0,0 +1,26 @@ +#app-container { + text-align: center; + width: 100vw; + height: 100vh; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +.App { + width: 100vw; + height: 100vh; + font-size: calc(10px + 2vmin); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +code { + background: rgba(148, 163, 184, 0.5); + border-radius: 0.25rem; + padding: 0.2rem 0.5rem; +} diff --git a/pages/options/src/Options.tsx b/pages/options/src/Options.tsx new file mode 100644 index 0000000..09d8263 --- /dev/null +++ b/pages/options/src/Options.tsx @@ -0,0 +1,26 @@ +import '@src/Options.css'; +import { t } from '@extension/i18n'; +import { PROJECT_URL_OBJECT, useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; + +const Options = () => { + const { isLight } = useStorage(exampleThemeStorage); + const logo = isLight ? 'options/logo_horizontal.svg' : 'options/logo_horizontal_dark.svg'; + + const goGithubSite = () => chrome.tabs.create(PROJECT_URL_OBJECT); + + return ( +
+ +

+ Edit pages/options/src/Options.tsx +

+ {t('toggleTheme')} +
+ ); +}; + +export default withErrorBoundary(withSuspense(Options, ), ErrorDisplay); diff --git a/pages/options/src/index.css b/pages/options/src/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/options/src/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/options/src/index.tsx b/pages/options/src/index.tsx new file mode 100644 index 0000000..a0b576c --- /dev/null +++ b/pages/options/src/index.tsx @@ -0,0 +1,14 @@ +import '@src/index.css'; +import Options from '@src/Options'; +import { createRoot } from 'react-dom/client'; + +const init = () => { + const appContainer = document.querySelector('#app-container'); + if (!appContainer) { + throw new Error('Can not find #app-container'); + } + const root = createRoot(appContainer); + root.render(); +}; + +init(); diff --git a/pages/options/tailwind.config.ts b/pages/options/tailwind.config.ts new file mode 100644 index 0000000..3418ee6 --- /dev/null +++ b/pages/options/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['index.html', 'src/**/*.tsx'], +}); diff --git a/pages/options/tsconfig.json b/pages/options/tsconfig.json new file mode 100644 index 0000000..b85f4eb --- /dev/null +++ b/pages/options/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts", "tailwind.config.ts"] +} diff --git a/pages/options/vite.config.mts b/pages/options/vite.config.mts new file mode 100644 index 0000000..6220f06 --- /dev/null +++ b/pages/options/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'options'), + }, +}); diff --git a/pages/popup/index.html b/pages/popup/index.html new file mode 100644 index 0000000..f75fabf --- /dev/null +++ b/pages/popup/index.html @@ -0,0 +1,12 @@ + + + + + Popup + + + +
+ + + diff --git a/pages/popup/package.json b/pages/popup/package.json new file mode 100644 index 0000000..4b1d554 --- /dev/null +++ b/pages/popup/package.json @@ -0,0 +1,39 @@ +{ + "name": "@extension/popup", + "version": "0.5.0", + "description": "chrome extension - popup", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/i18n": "workspace:*", + "@extension/ui": "workspace:*" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } + } +} diff --git a/pages/popup/public/logo_vertical.svg b/pages/popup/public/logo_vertical.svg new file mode 100644 index 0000000..5768b87 --- /dev/null +++ b/pages/popup/public/logo_vertical.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/popup/public/logo_vertical_dark.svg b/pages/popup/public/logo_vertical_dark.svg new file mode 100644 index 0000000..b4089d8 --- /dev/null +++ b/pages/popup/public/logo_vertical_dark.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/popup/src/Popup.css b/pages/popup/src/Popup.css new file mode 100644 index 0000000..e29c75f --- /dev/null +++ b/pages/popup/src/Popup.css @@ -0,0 +1,30 @@ +.App { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + text-align: center; + height: 100%; + padding: 1rem; +} + +.App-logo { + height: 50vmin; + margin-bottom: 1rem; +} + +.App-header { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + font-size: 0.75rem; +} + +code { + background: rgba(148, 163, 184, 0.5); + border-radius: 0.25rem; + padding: 0.2rem 0.5rem; +} diff --git a/pages/popup/src/Popup.tsx b/pages/popup/src/Popup.tsx new file mode 100644 index 0000000..192339c --- /dev/null +++ b/pages/popup/src/Popup.tsx @@ -0,0 +1,63 @@ +import '@src/Popup.css'; +import { t } from '@extension/i18n'; +import { PROJECT_URL_OBJECT, useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; + +const notificationOptions = { + type: 'basic', + iconUrl: chrome.runtime.getURL('icon-34.png'), + title: 'Injecting content script error', + message: 'You cannot inject script here!', +} as const; + +const Popup = () => { + const { isLight } = useStorage(exampleThemeStorage); + const logo = isLight ? 'popup/logo_vertical.svg' : 'popup/logo_vertical_dark.svg'; + + const goGithubSite = () => chrome.tabs.create(PROJECT_URL_OBJECT); + + const injectContentScript = async () => { + const [tab] = await chrome.tabs.query({ currentWindow: true, active: true }); + + if (tab.url!.startsWith('about:') || tab.url!.startsWith('chrome:')) { + chrome.notifications.create('inject-error', notificationOptions); + } + + await chrome.scripting + .executeScript({ + target: { tabId: tab.id! }, + files: ['/content-runtime/example.iife.js', '/content-runtime/all.iife.js'], + }) + .catch(err => { + // Handling errors related to other paths + if (err.message.includes('Cannot access a chrome:// URL')) { + chrome.notifications.create('inject-error', notificationOptions); + } + }); + }; + + return ( +
+
+ +

+ Edit pages/popup/src/Popup.tsx +

+ + {t('toggleTheme')} +
+
+ ); +}; + +export default withErrorBoundary(withSuspense(Popup, ), ErrorDisplay); diff --git a/pages/popup/src/index.css b/pages/popup/src/index.css new file mode 100644 index 0000000..0f6ad88 --- /dev/null +++ b/pages/popup/src/index.css @@ -0,0 +1,6 @@ +@import '@extension/ui/global.css'; + +body { + width: 300px; + height: 260px; +} diff --git a/pages/popup/src/index.tsx b/pages/popup/src/index.tsx new file mode 100644 index 0000000..afd2e44 --- /dev/null +++ b/pages/popup/src/index.tsx @@ -0,0 +1,15 @@ +import '@src/index.css'; +import Popup from '@src/Popup'; +import { createRoot } from 'react-dom/client'; + +const init = () => { + const appContainer = document.querySelector('#app-container'); + if (!appContainer) { + throw new Error('Can not find #app-container'); + } + const root = createRoot(appContainer); + + root.render(); +}; + +init(); diff --git a/pages/popup/tailwind.config.ts b/pages/popup/tailwind.config.ts new file mode 100644 index 0000000..3418ee6 --- /dev/null +++ b/pages/popup/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['index.html', 'src/**/*.tsx'], +}); diff --git a/pages/popup/tsconfig.json b/pages/popup/tsconfig.json new file mode 100644 index 0000000..b85f4eb --- /dev/null +++ b/pages/popup/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts", "tailwind.config.ts"] +} diff --git a/pages/popup/vite.config.mts b/pages/popup/vite.config.mts new file mode 100644 index 0000000..8c860b9 --- /dev/null +++ b/pages/popup/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'popup'), + }, +}); diff --git a/pages/side-panel/index.html b/pages/side-panel/index.html new file mode 100644 index 0000000..27859dc --- /dev/null +++ b/pages/side-panel/index.html @@ -0,0 +1,12 @@ + + + + + Side Panel + + + +
+ + + diff --git a/pages/side-panel/package.json b/pages/side-panel/package.json new file mode 100644 index 0000000..289a299 --- /dev/null +++ b/pages/side-panel/package.json @@ -0,0 +1,39 @@ +{ + "name": "@extension/sidepanel", + "version": "0.5.0", + "description": "chrome extension - side panel", + "type": "module", + "private": true, + "sideEffects": true, + "files": [ + "dist/**" + ], + "scripts": { + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules", + "build": "vite build", + "dev": "vite build --mode development", + "lint": "eslint .", + "lint:fix": "pnpm lint --fix", + "format": "prettier . --write --ignore-path ../../.prettierignore", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@extension/shared": "workspace:*", + "@extension/storage": "workspace:*", + "@extension/i18n": "workspace:*", + "@extension/ui": "workspace:*" + }, + "devDependencies": { + "@extension/tailwindcss-config": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@extension/vite-config": "workspace:*" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } + } +} diff --git a/pages/side-panel/public/logo_vertical.svg b/pages/side-panel/public/logo_vertical.svg new file mode 100644 index 0000000..5768b87 --- /dev/null +++ b/pages/side-panel/public/logo_vertical.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/side-panel/public/logo_vertical_dark.svg b/pages/side-panel/public/logo_vertical_dark.svg new file mode 100644 index 0000000..b4089d8 --- /dev/null +++ b/pages/side-panel/public/logo_vertical_dark.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/side-panel/src/SidePanel.css b/pages/side-panel/src/SidePanel.css new file mode 100644 index 0000000..55fb324 --- /dev/null +++ b/pages/side-panel/src/SidePanel.css @@ -0,0 +1,30 @@ +.App { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + text-align: center; + height: 100%; + padding: 1rem; +} + +.App-logo { + height: 50vmin; + margin-bottom: 1rem; +} + +.App-header { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); +} + +code { + background: rgba(148, 163, 184, 0.5); + border-radius: 0.25rem; + padding: 0.2rem 0.5rem; +} diff --git a/pages/side-panel/src/SidePanel.tsx b/pages/side-panel/src/SidePanel.tsx new file mode 100644 index 0000000..3ed3563 --- /dev/null +++ b/pages/side-panel/src/SidePanel.tsx @@ -0,0 +1,28 @@ +import '@src/SidePanel.css'; +import { t } from '@extension/i18n'; +import { PROJECT_URL_OBJECT, useStorage, withErrorBoundary, withSuspense } from '@extension/shared'; +import { exampleThemeStorage } from '@extension/storage'; +import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui'; + +const SidePanel = () => { + const { isLight } = useStorage(exampleThemeStorage); + const logo = isLight ? 'side-panel/logo_vertical.svg' : 'side-panel/logo_vertical_dark.svg'; + + const goGithubSite = () => chrome.tabs.create(PROJECT_URL_OBJECT); + + return ( +
+
+ +

+ Edit pages/side-panel/src/SidePanel.tsx +

+ {t('toggleTheme')} +
+
+ ); +}; + +export default withErrorBoundary(withSuspense(SidePanel, ), ErrorDisplay); diff --git a/pages/side-panel/src/index.css b/pages/side-panel/src/index.css new file mode 100644 index 0000000..e71af25 --- /dev/null +++ b/pages/side-panel/src/index.css @@ -0,0 +1 @@ +@import '@extension/ui/global.css'; diff --git a/pages/side-panel/src/index.tsx b/pages/side-panel/src/index.tsx new file mode 100644 index 0000000..350ac23 --- /dev/null +++ b/pages/side-panel/src/index.tsx @@ -0,0 +1,14 @@ +import '@src/index.css'; +import SidePanel from '@src/SidePanel'; +import { createRoot } from 'react-dom/client'; + +const init = () => { + const appContainer = document.querySelector('#app-container'); + if (!appContainer) { + throw new Error('Can not find #app-container'); + } + const root = createRoot(appContainer); + root.render(); +}; + +init(); diff --git a/pages/side-panel/tailwind.config.ts b/pages/side-panel/tailwind.config.ts new file mode 100644 index 0000000..3418ee6 --- /dev/null +++ b/pages/side-panel/tailwind.config.ts @@ -0,0 +1,5 @@ +import { withUI } from '@extension/ui'; + +export default withUI({ + content: ['index.html', 'src/**/*.tsx'], +}); diff --git a/pages/side-panel/tsconfig.json b/pages/side-panel/tsconfig.json new file mode 100644 index 0000000..b85f4eb --- /dev/null +++ b/pages/side-panel/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@extension/tsconfig/base", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + }, + "types": ["chrome", "node"] + }, + "include": ["src", "vite.config.mts", "tailwind.config.ts"] +} diff --git a/pages/side-panel/vite.config.mts b/pages/side-panel/vite.config.mts new file mode 100644 index 0000000..c6d889c --- /dev/null +++ b/pages/side-panel/vite.config.mts @@ -0,0 +1,17 @@ +import { resolve } from 'node:path'; +import { withPageConfig } from '@extension/vite-config'; + +const rootDir = resolve(import.meta.dirname); +const srcDir = resolve(rootDir, 'src'); + +export default withPageConfig({ + resolve: { + alias: { + '@src': srcDir, + }, + }, + publicDir: resolve(rootDir, 'public'), + build: { + outDir: resolve(rootDir, '..', '..', 'dist', 'side-panel'), + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..712a169 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,10095 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + react: + specifier: ^19.1.0 + version: 19.1.1 + react-dom: + specifier: ^19.1.0 + version: 19.1.1(react@19.1.1) + devDependencies: + '@eslint/compat': + specifier: ^1.2.9 + version: 1.3.2(eslint@9.35.0(jiti@2.5.1)) + '@eslint/eslintrc': + specifier: ^3.3.1 + version: 3.3.1 + '@eslint/js': + specifier: ^9.27.0 + version: 9.35.0 + '@types/chrome': + specifier: ^0.0.323 + version: 0.0.323 + '@types/eslint-plugin-jsx-a11y': + specifier: ^6.10.0 + version: 6.10.0 + '@types/node': + specifier: ^22.15.21 + version: 22.18.1 + '@types/react': + specifier: ^19.1.5 + version: 19.1.12 + '@types/react-dom': + specifier: ^19.1.5 + version: 19.1.9(@types/react@19.1.12) + '@typescript-eslint/parser': + specifier: ^8.32.1 + version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + deepmerge: + specifier: ^4.3.1 + version: 4.3.1 + eslint: + specifier: ^9.27.0 + version: 9.35.0(jiti@2.5.1) + eslint-config-prettier: + specifier: ^10.1.5 + version: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + eslint-import-resolver-typescript: + specifier: ^4.3.5 + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-import-x: + specifier: ^4.12.2 + version: 4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-jsx-a11y: + specifier: ^6.10.2 + version: 6.10.2(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-prettier: + specifier: ^5.4.0 + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-tailwindcss: + specifier: ^3.18.0 + version: 3.18.2(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2))) + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 + fflate: + specifier: ^0.8.2 + version: 0.8.2 + globals: + specifier: ^16.1.0 + version: 16.4.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + jiti: + specifier: ^2.4.2 + version: 2.5.1 + lint-staged: + specifier: ^16.0.0 + version: 16.1.6 + postcss: + specifier: ^8.5.3 + version: 8.5.6 + postcss-load-config: + specifier: ^6.0.1 + version: 6.0.1(jiti@2.5.1)(postcss@8.5.6)(tsx@4.20.5)(yaml@2.8.1) + prettier: + specifier: ^3.5.3 + version: 3.6.2 + prettier-plugin-tailwindcss: + specifier: ^0.6.11 + version: 0.6.14(prettier@3.6.2) + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + run-script-os: + specifier: ^1.1.6 + version: 1.1.6 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2)) + tslib: + specifier: ^2.8.1 + version: 2.8.1 + tsx: + specifier: ^4.19.4 + version: 4.20.5 + turbo: + specifier: ^2.5.3 + version: 2.5.6 + typescript: + specifier: ^5.8.3 + version: 5.9.2 + typescript-eslint: + specifier: ^8.32.1 + version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + vite: + specifier: ^6.3.6 + version: 6.3.6(@types/node@22.18.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1) + vite-plugin-node-polyfills: + specifier: ^0.23.0 + version: 0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.18.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)) + + chrome-extension: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../packages/env + '@extension/shared': + specifier: workspace:* + version: link:../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../packages/storage + webextension-polyfill: + specifier: ^0.12.0 + version: 0.12.0 + devDependencies: + '@extension/dev-utils': + specifier: workspace:* + version: link:../packages/dev-utils + '@extension/hmr': + specifier: workspace:* + version: link:../packages/hmr + '@extension/tsconfig': + specifier: workspace:* + version: link:../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../packages/vite-config + '@laynezh/vite-plugin-lib-assets': + specifier: ^1.1.0 + version: 1.2.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)) + magic-string: + specifier: ^0.30.17 + version: 0.30.19 + ts-loader: + specifier: ^9.5.2 + version: 9.5.4(typescript@5.9.2)(webpack@5.94.0(@swc/core@1.13.5)) + + packages/dev-utils: + devDependencies: + '@extension/shared': + specifier: workspace:* + version: link:../shared + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + packages/env: + dependencies: + '@dotenvx/dotenvx': + specifier: ^1.44.1 + version: 1.49.0 + devDependencies: + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + packages/hmr: + devDependencies: + '@extension/env': + specifier: workspace:* + version: link:../env + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + '@rollup/plugin-sucrase': + specifier: ^5.0.2 + version: 5.0.2(rollup@4.50.1) + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + esbuild: + specifier: ^0.25.4 + version: 0.25.9 + esm: + specifier: ^3.2.25 + version: 3.2.25 + rollup: + specifier: ^4.41.0 + version: 4.50.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.13.5)(@types/node@24.3.1)(typescript@5.9.2) + ws: + specifier: ^8.18.2 + version: 8.18.3 + + packages/i18n: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../env + devDependencies: + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + packages/module-manager: + devDependencies: + '@extension/dev-utils': + specifier: workspace:* + version: link:../dev-utils + '@extension/shared': + specifier: workspace:* + version: link:../shared + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + '@inquirer/prompts': + specifier: ^7.5.1 + version: 7.8.4(@types/node@24.3.1) + '@types/yargs': + specifier: ^17.0.33 + version: 17.0.33 + tsx: + specifier: ^4.19.4 + version: 4.20.5 + yargs: + specifier: ^17.7.2 + version: 17.7.2 + + packages/shared: + dependencies: + react-error-boundary: + specifier: ^6.0.0 + version: 6.0.0(react@19.1.1) + devDependencies: + '@extension/storage': + specifier: workspace:* + version: link:../storage + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + type-fest: + specifier: ^4.41.0 + version: 4.41.0 + + packages/storage: + devDependencies: + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + packages/tailwindcss-config: + devDependencies: + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + packages/tsconfig: {} + + packages/ui: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../i18n + '@extension/shared': + specifier: workspace:* + version: link:../shared + '@extension/storage': + specifier: workspace:* + version: link:../storage + clsx: + specifier: ^2.1.1 + version: 2.1.1 + tailwind-merge: + specifier: ^3.3.0 + version: 3.3.1 + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + react-spinners: + specifier: ^0.17.0 + version: 0.17.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + tsc-alias: + specifier: ^1.8.16 + version: 1.8.16 + + packages/vite-config: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../env + devDependencies: + '@extension/hmr': + specifier: workspace:* + version: link:../hmr + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + '@vitejs/plugin-react-swc': + specifier: ^3.9.0 + version: 3.11.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)) + + packages/zipper: + devDependencies: + '@extension/dev-utils': + specifier: workspace:* + version: link:../dev-utils + '@extension/env': + specifier: workspace:* + version: link:../env + '@extension/tsconfig': + specifier: workspace:* + version: link:../tsconfig + + pages/content: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../../packages/env + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + devDependencies: + '@extension/hmr': + specifier: workspace:* + version: link:../../packages/hmr + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/content-runtime: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../../packages/env + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/hmr': + specifier: workspace:* + version: link:../../packages/hmr + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/content-ui: + dependencies: + '@extension/env': + specifier: workspace:* + version: link:../../packages/env + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/hmr': + specifier: workspace:* + version: link:../../packages/hmr + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/devtools: + dependencies: + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + devDependencies: + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/devtools-panel: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/new-tab: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + sass: + specifier: ^1.89.0 + version: 1.92.1 + + pages/options: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/popup: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + pages/side-panel: + dependencies: + '@extension/i18n': + specifier: workspace:* + version: link:../../packages/i18n + '@extension/shared': + specifier: workspace:* + version: link:../../packages/shared + '@extension/storage': + specifier: workspace:* + version: link:../../packages/storage + '@extension/ui': + specifier: workspace:* + version: link:../../packages/ui + devDependencies: + '@extension/tailwindcss-config': + specifier: workspace:* + version: link:../../packages/tailwindcss-config + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@extension/vite-config': + specifier: workspace:* + version: link:../../packages/vite-config + + tests/e2e: + devDependencies: + '@extension/env': + specifier: workspace:* + version: link:../../packages/env + '@extension/tsconfig': + specifier: workspace:* + version: link:../../packages/tsconfig + '@wdio/cli': + specifier: ^9.14.0 + version: 9.19.2(@types/node@24.3.1)(expect-webdriverio@5.4.2) + '@wdio/globals': + specifier: ^9.14.0 + version: 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2) + '@wdio/local-runner': + specifier: ^9.14.0 + version: 9.19.2(@wdio/globals@9.17.0)(webdriverio@9.19.2) + '@wdio/mocha-framework': + specifier: ^9.14.0 + version: 9.19.2 + '@wdio/spec-reporter': + specifier: ^9.14.0 + version: 9.19.2 + '@wdio/types': + specifier: ^9.14.0 + version: 9.19.2 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.27.1': + resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + engines: {node: '>=6.9.0'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dotenvx/dotenvx@1.49.0': + resolution: {integrity: sha512-M1cyP6YstFQCjih54SAxCqHLMMi8QqV8tenpgGE48RTXWD7vfMYJiw/6xcCDpS2h28AcLpTsFCZA863Ge9yxzA==} + hasBin: true + + '@ecies/ciphers@0.2.3': + resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + + '@emnapi/core@1.5.0': + resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.3.2': + resolution: {integrity: sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.35.0': + resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/checkbox@4.2.2': + resolution: {integrity: sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.16': + resolution: {integrity: sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.2.0': + resolution: {integrity: sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.18': + resolution: {integrity: sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.18': + resolution: {integrity: sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.1': + resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.13': + resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} + engines: {node: '>=18'} + + '@inquirer/input@4.2.2': + resolution: {integrity: sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.18': + resolution: {integrity: sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.18': + resolution: {integrity: sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.8.4': + resolution: {integrity: sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.6': + resolution: {integrity: sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.1.1': + resolution: {integrity: sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.3.2': + resolution: {integrity: sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.8': + resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.1.2': + resolution: {integrity: sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.0.5': + resolution: {integrity: sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@laynezh/vite-plugin-lib-assets@1.2.0': + resolution: {integrity: sha512-rmiy9yreGSrzU5HEMGX1gbQ9nA03I/3YaOpmwB1dOMEYT44ijyN6QD0VJ8bUNC2BA/fd1pYCkkgr2eFlvo0G2Q==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@promptbook/utils@0.69.5': + resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==} + + '@puppeteer/browsers@2.10.8': + resolution: {integrity: sha512-f02QYEnBDE0p8cteNoPYHHjbDuwyfbe4cCIVlNi8/MRicIxFW4w4CfgU0LNgWEID6s06P+hRJ1qjpBLMhPRCiQ==} + engines: {node: '>=18'} + hasBin: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-sucrase@5.0.2': + resolution: {integrity: sha512-4MhIVH9Dy2Hwose1/x5QMs0XF7yn9jDd/yozHqzdIrMWIolgFpGnrnVhQkqTaK1RALY/fpyrEKmwH/04vr1THA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.53.1||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@swc/core-darwin-arm64@1.13.5': + resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.13.5': + resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.13.5': + resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.13.5': + resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.13.5': + resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.13.5': + resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.13.5': + resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.13.5': + resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.13.5': + resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.13.5': + resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.13.5': + resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + + '@types/chrome@0.0.323': + resolution: {integrity: sha512-ipiDwx41lmGeLnbiT6ENOayvWXdkqKqNwqDQWEuz6dujaX7slSkk1nbSt5Q5c6xnQ708+kuCFrC00VLltSbWVA==} + + '@types/eslint-plugin-jsx-a11y@6.10.0': + resolution: {integrity: sha512-TGKmk2gO6DrvTVADNOGQMqn3SzqcFcJILFnXNllQA34us9uClS3/AsL/cERPz6jS9ePI3bx+1q8/d2GZsxPVYw==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/filesystem@0.0.36': + resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} + + '@types/filewriter@0.0.33': + resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} + + '@types/har-format@1.2.16': + resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + + '@types/node@20.17.50': + resolution: {integrity: sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==} + + '@types/node@20.19.13': + resolution: {integrity: sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==} + + '@types/node@22.15.21': + resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} + + '@types/node@22.18.1': + resolution: {integrity: sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw==} + + '@types/node@24.3.1': + resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/react-dom@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.12': + resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} + + '@types/sinonjs__fake-timers@8.1.5': + resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/which@2.0.2': + resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@8.43.0': + resolution: {integrity: sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.43.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.43.0': + resolution: {integrity: sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.43.0': + resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.43.0': + resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.43.0': + resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.43.0': + resolution: {integrity: sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.43.0': + resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.43.0': + resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.43.0': + resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.43.0': + resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vitejs/plugin-react-swc@3.11.0': + resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} + peerDependencies: + vite: ^4 || ^5 || ^6 || ^7 + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@wdio/cli@9.19.2': + resolution: {integrity: sha512-GR011VfgW57tCycaTYzYD74eJnMebqWtVeHrBNLWgLTIl3bVMIkBGPEO7jjQJSZum3p1rxIJKoy+49Xc4YcY2g==} + engines: {node: '>=18.20.0'} + hasBin: true + + '@wdio/config@9.19.2': + resolution: {integrity: sha512-OVCzPQxav0QDk5rktQ6LYARZ5ueUuJXIqTXUpS3A9Jt6PF+ZUI5sbO/y+z+qHQXqDq+LkscmFsmkzgnoHzHcfg==} + engines: {node: '>=18.20.0'} + + '@wdio/dot-reporter@9.19.2': + resolution: {integrity: sha512-r7SIq3e9o1LVzb1z+r0uN7q693Etx1gbRG/frs6mgIlEnyqd0pd45Z5B6KxwjEVCYp472hs+rujArZrDyx3sfQ==} + engines: {node: '>=18.20.0'} + + '@wdio/globals@9.17.0': + resolution: {integrity: sha512-i38o7wlipLllNrk2hzdDfAmk6nrqm3lR2MtAgWgtHbwznZAKkB84KpkNFfmUXw5Kg3iP1zKlSjwZpKqenuLc+Q==} + engines: {node: '>=18.20.0'} + peerDependencies: + expect-webdriverio: ^5.3.4 + webdriverio: ^9.0.0 + + '@wdio/local-runner@9.19.2': + resolution: {integrity: sha512-P1n8970iuEIrZyXsuVBqGZFy1vzXdx3C46/ErqWPcPzpaf/1X4hVx199etJb6RdnqFNejAn8T8OWQlFjF4owMQ==} + engines: {node: '>=18.20.0'} + + '@wdio/logger@9.18.0': + resolution: {integrity: sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==} + engines: {node: '>=18.20.0'} + + '@wdio/mocha-framework@9.19.2': + resolution: {integrity: sha512-aW08G8OjQfKNCiaLw74Qzl7/UiJBEcd9osxB8ZoMlsL9Aem2FqN523X+b92kk6hQQSfZporstulP3HPBRatVqw==} + engines: {node: '>=18.20.0'} + + '@wdio/protocols@9.16.2': + resolution: {integrity: sha512-h3k97/lzmyw5MowqceAuY3HX/wGJojXHkiPXA3WlhGPCaa2h4+GovV2nJtRvknCKsE7UHA1xB5SWeI8MzloBew==} + + '@wdio/repl@9.16.2': + resolution: {integrity: sha512-FLTF0VL6+o5BSTCO7yLSXocm3kUnu31zYwzdsz4n9s5YWt83sCtzGZlZpt7TaTzb3jVUfxuHNQDTb8UMkCu0lQ==} + engines: {node: '>=18.20.0'} + + '@wdio/reporter@9.19.2': + resolution: {integrity: sha512-57xvKib6lOwZyJgLeJW2IHmJxILAA0hlXLZs/cB+DTjsYt6ZwXQJ4SqVm4hF2H9bPGERAzAueLsbVocH5N9Y3Q==} + engines: {node: '>=18.20.0'} + + '@wdio/runner@9.19.2': + resolution: {integrity: sha512-1ikVrf2FeCqTGdZlvDeQE96WtAzepj5OTRt8zaC63erHZP4OQi8qXCnlS2x+EWhGI+KDPKByyCeT//FmWMobHw==} + engines: {node: '>=18.20.0'} + peerDependencies: + expect-webdriverio: ^5.3.4 + webdriverio: ^9.0.0 + + '@wdio/spec-reporter@9.19.2': + resolution: {integrity: sha512-q03tzZNMQU9+FnoYdnc//W6iO3qgL0+xnS1EyhRpHFlWGcPfDhZdnejqUtiYDWeB9v+FSRMhptT7C+R+QpxEZQ==} + engines: {node: '>=18.20.0'} + + '@wdio/types@9.19.2': + resolution: {integrity: sha512-fBI7ljL+YcPXSXUhdk2+zVuz7IYP1aDMTq1eVmMme9GY0y67t0dCNPOt6xkCAEdL5dOcV6D2L1r6Cf/M2ifTvQ==} + engines: {node: '>=18.20.0'} + + '@wdio/utils@9.19.2': + resolution: {integrity: sha512-caimJiTsxDUfXn/gRAzcYTO3RydSl7XzD+QpjfWZYJjzr8a2XfNnj+Vdmr8gG4BSkiVHirW9mFCZeQp2eTD7rA==} + engines: {node: '>=18.20.0'} + + '@wdio/xvfb@9.19.2': + resolution: {integrity: sha512-RwvYNmhGzFjcUjM6OXbCzp71hAmgIGT9upN149aQ3qcgVEHvuFX6Ybxtc3TMmt76bA2wzg1KC7i3G/KYIOEndw==} + engines: {node: '>=18'} + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@zip.js/zip.js@2.8.2': + resolution: {integrity: sha512-PI6UdgpSeVoGvzguKHmy2bwOqI3UYkntLZOCpyJSKIi7234c5aJmQYkJB/P4P2YUJkqhbqvu7iM2/0eJZ178nA==} + engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.1.0: + resolution: {integrity: sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-exit-hook@2.0.1: + resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} + engines: {node: '>=0.12.0'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.10.3: + resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.6.1: + resolution: {integrity: sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==} + + bare-fs@4.3.2: + resolution: {integrity: sha512-FAJ00JF69O6/oKAP+oiJYgdem1biZoGAR0NbRkBRQZ26shA87DmdHWbpeY3EVKPrAzHByLoLo+hAzTT6NTJWCg==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + bare-url: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-url: + optional: true + + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bn.js@4.12.2: + resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + + bn.js@5.2.2: + resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.1: + resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} + engines: {node: '>= 0.10'} + + browserify-sign@4.2.3: + resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} + engines: {node: '>= 0.12'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.24.5: + resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} + engines: {node: '>=8'} + + cipher-base@1.0.6: + resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==} + engines: {node: '>= 0.10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + create-wdio@9.18.2: + resolution: {integrity: sha512-atf81YJfyTNAJXsNu3qhpqF4OO43tHGTpr88duAc1Hk4a0uXJAPUYLnYxshOuMnfmeAxlWD+NqGU7orRiXEuJg==} + engines: {node: '>=12.0.0'} + hasBin: true + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-browserify@3.12.1: + resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} + engines: {node: '>= 0.10'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-shorthand-properties@1.1.2: + resolution: {integrity: sha512-C2AugXIpRGQTxaCW0N7n5jD/p5irUmCrwl03TrnMFBHDbdq44CFWR2zO7rK9xPN4Eo3pUxC4vQzQgbIpzrD1PQ==} + + css-value@0.0.1: + resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decamelize@6.0.1: + resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@4.22.0: + resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} + engines: {node: '>=10'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + easy-table@1.2.0: + resolution: {integrity: sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==} + + eciesjs@0.4.15: + resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + + edge-paths@3.0.5: + resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} + engines: {node: '>=14.0.0'} + + edgedriver@6.1.2: + resolution: {integrity: sha512-UvFqd/IR81iPyWMcxXbUNi+xKWR7JjfoHjfuwjqsj9UHQKn80RpQmS0jf+U25IPi+gKVPcpOSKm0XkqgGMq4zQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.155: + resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} + + electron-to-chromium@1.5.215: + resolution: {integrity: sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + emoji-regex@10.5.0: + resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-context@0.1.9: + resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@4.4.4: + resolution: {integrity: sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==} + engines: {node: ^16.17.0 || >=18.6.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import-x@4.16.1: + resolution: {integrity: sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/utils': ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + eslint-import-resolver-node: '*' + peerDependenciesMeta: + '@typescript-eslint/utils': + optional: true + eslint-import-resolver-node: + optional: true + + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-plugin-tailwindcss@3.18.2: + resolution: {integrity: sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==} + engines: {node: '>=18.12.0'} + peerDependencies: + tailwindcss: ^3.4.0 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.35.0: + resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + + exit-hook@4.0.0: + resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==} + engines: {node: '>=18'} + + expect-webdriverio@5.4.2: + resolution: {integrity: sha512-7bc5I2dU3onKJaRhBdxKh/C+W+ot7R+RcRMCLTSR7cbfHM9Shk8ocbNDVvjrxaBdA52kbZONVSyhexp7cq2xNA==} + engines: {node: '>=18 || >=20 || >=22'} + peerDependencies: + '@wdio/globals': ^9.0.0 + '@wdio/logger': ^9.0.0 + webdriverio: ^9.0.0 + + expect@30.1.2: + resolution: {integrity: sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + geckodriver@5.0.0: + resolution: {integrity: sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@11.0.2: + resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} + engines: {node: 20 || >=22} + hasBin: true + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.5: + resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} + engines: {node: '>= 0.10'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + + htmlfy@0.8.1: + resolution: {integrity: sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + immutable@5.1.2: + resolution: {integrity: sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@12.9.4: + resolution: {integrity: sha512-5bV3LOgLtMAiJq1QpaUddfRrvaX59wiMYppS7z2jNRSQ64acI0yqx7WMxWhgymenSXOyD657g9tlsTjqGYM8sg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} + engines: {node: 20 || >=22} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jest-diff@30.1.2: + resolution: {integrity: sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.1.2: + resolution: {integrity: sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.1.0: + resolution: {integrity: sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.0.5: + resolution: {integrity: sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.0.5: + resolution: {integrity: sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lines-and-columns@2.0.4: + resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lint-staged@16.1.6: + resolution: {integrity: sha512-U4kuulU3CKIytlkLlaHcGgKscNfJPNTiDF2avIUGFCv7K95/DCYQ7Ra62ydeRWmgQGg9zJYw2dzdbztwJlqrow==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.3: + resolution: {integrity: sha512-0aeh5HHHgmq1KRdMMDHfhMWQmIT/m7nRDTlxlFqni2Sp0had9baqsjJRvDGdlvgd6NmPE0nPloOipiQJGFtTHQ==} + engines: {node: '>=20.0.0'} + + loader-runner@4.3.0: + resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + engines: {node: '>=6.11.5'} + + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + + locate-app@2.5.0: + resolution: {integrity: sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.flattendeep@4.4.0: + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.pickby@4.6.0: + resolution: {integrity: sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==} + + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loglevel-plugin-prefix@0.8.4: + resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + engines: {node: 20 || >=22} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mocha@10.8.2: + resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} + engines: {node: '>= 14.0.0'} + hasBin: true + + mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + mylas@2.1.13: + resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==} + engines: {node: '>=12.0.0'} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.3: + resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + node-releases@2.0.20: + resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} + + node-stdlib-browser@1.3.1: + resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} + engines: {node: '>=10'} + + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + + normalize-package-data@7.0.1: + resolution: {integrity: sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==} + engines: {node: ^18.17.0 || >=20.5.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-treeify@1.1.33: + resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} + engines: {node: '>= 10'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.7: + resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + engines: {node: '>= 0.10'} + + parse-json@7.1.1: + resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} + engines: {node: '>=16'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + plimit-lit@1.6.1: + resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier-plugin-tailwindcss@0.6.14: + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@30.0.5: + resolution: {integrity: sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-lit@1.5.2: + resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} + engines: {node: '>=12'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + + react-error-boundary@6.0.0: + resolution: {integrity: sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==} + peerDependencies: + react: '>=16.13.1' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-spinners@0.17.0: + resolution: {integrity: sha512-L/8HTylaBmIWwQzIjMq+0vyaRXuoAevzWoD35wKpNTxxtYXWZp+xtgkfD7Y4WItuX0YvdxMPU79+7VhhmbmuTQ==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react@19.1.1: + resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-pkg-up@10.1.0: + resolution: {integrity: sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==} + engines: {node: '>=16'} + + read-pkg@8.1.0: + resolution: {integrity: sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==} + engines: {node: '>=16'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recursive-readdir@2.2.3: + resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} + engines: {node: '>=6.0.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + resq@1.11.0: + resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + ret@0.5.0: + resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} + engines: {node: '>=10'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rgb2hex@0.2.5: + resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} + + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-async@4.0.6: + resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + run-script-os@1.1.6: + resolution: {integrity: sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==} + hasBin: true + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safaridriver@1.0.0: + resolution: {integrity: sha512-J92IFbskyo7OYB3Dt4aTdyhag1GlInrfbPCmMteb7aBK7PwlnGz1HI0+oyNN97j7pV9DqUAVoVgkNRMrfY47mQ==} + engines: {node: '>=18.0.0'} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-regex2@5.0.0: + resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass@1.92.1: + resolution: {integrity: sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.2: + resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + engines: {node: '>= 10.13.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + serialize-error@12.0.0: + resolution: {integrity: sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==} + engines: {node: '>=18'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + spacetrim@0.11.59: + resolution: {integrity: sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stable-hash-x@0.2.0: + resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} + engines: {node: '>=12.0.0'} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-buffers@3.0.3: + resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} + engines: {node: '>= 0.10.0'} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.1.1: + resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + tar-fs@3.1.0: + resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + engines: {node: '>=10'} + hasBin: true + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + ts-loader@9.5.4: + resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-alias@1.8.16: + resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==} + engines: {node: '>=16.20.2'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + turbo-darwin-64@2.5.6: + resolution: {integrity: sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.5.6: + resolution: {integrity: sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.5.6: + resolution: {integrity: sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.5.6: + resolution: {integrity: sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.5.6: + resolution: {integrity: sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.5.6: + resolution: {integrity: sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q==} + cpu: [arm64] + os: [win32] + + turbo@2.5.6: + resolution: {integrity: sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w==} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-fest@4.26.0: + resolution: {integrity: sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==} + engines: {node: '>=16'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.43.0: + resolution: {integrity: sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + + undici@6.21.3: + resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} + engines: {node: '>=18.17'} + + undici@7.16.0: + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + engines: {node: '>=20.18.1'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} + + userhome@1.0.1: + resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==} + engines: {node: '>= 0.8.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + vite-plugin-node-polyfills@0.23.0: + resolution: {integrity: sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + + vite@6.3.6: + resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + 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 + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + engines: {node: '>=10.13.0'} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webdriver@9.19.2: + resolution: {integrity: sha512-kw6dSwNzimU8/CkGVlM36pqWHZ7BhCwV4/d8fu6rpIYGeQbPwcNc4M90TfJuzYMA7Au3NdrwT/EVQgVLQ9Ju8Q==} + engines: {node: '>=18.20.0'} + + webdriverio@9.19.2: + resolution: {integrity: sha512-xP/9odQ9tt2pEuMgo0Oobklhu1lObgL1KmejZeyxVStwnrSTbFmn1AAqPq5pfXizUsyv2PR5+id9frrarx/c4w==} + engines: {node: '>=18.20.0'} + peerDependencies: + puppeteer-core: '>=22.x || <=24.x' + peerDependenciesMeta: + puppeteer-core: + optional: true + + webextension-polyfill@0.12.0: + resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack@5.94.0: + resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + 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 + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/runtime@7.27.1': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@dotenvx/dotenvx@1.49.0': + dependencies: + commander: 11.1.0 + dotenv: 17.2.2 + eciesjs: 0.4.15 + execa: 5.1.1 + fdir: 6.4.4(picomatch@4.0.2) + ignore: 5.3.2 + object-treeify: 1.1.33 + picomatch: 4.0.2 + which: 4.0.0 + + '@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + + '@emnapi/core@1.5.0': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.25.9': + optional: true + + '@esbuild/android-arm@0.25.9': + optional: true + + '@esbuild/android-x64@0.25.9': + optional: true + + '@esbuild/darwin-arm64@0.25.9': + optional: true + + '@esbuild/darwin-x64@0.25.9': + optional: true + + '@esbuild/freebsd-arm64@0.25.9': + optional: true + + '@esbuild/freebsd-x64@0.25.9': + optional: true + + '@esbuild/linux-arm64@0.25.9': + optional: true + + '@esbuild/linux-arm@0.25.9': + optional: true + + '@esbuild/linux-ia32@0.25.9': + optional: true + + '@esbuild/linux-loong64@0.25.9': + optional: true + + '@esbuild/linux-mips64el@0.25.9': + optional: true + + '@esbuild/linux-ppc64@0.25.9': + optional: true + + '@esbuild/linux-riscv64@0.25.9': + optional: true + + '@esbuild/linux-s390x@0.25.9': + optional: true + + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': + optional: true + + '@esbuild/netbsd-x64@0.25.9': + optional: true + + '@esbuild/openbsd-arm64@0.25.9': + optional: true + + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.25.9': + optional: true + + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0(jiti@2.5.1))': + dependencies: + eslint: 9.35.0(jiti@2.5.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.3.2(eslint@9.35.0(jiti@2.5.1))': + optionalDependencies: + eslint: 9.35.0(jiti@2.5.1) + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1(supports-color@8.1.1) + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.35.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/checkbox@4.2.2(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.1) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/confirm@5.1.16(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/core@10.2.0(@types/node@24.3.1)': + dependencies: + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.1) + ansi-escapes: 4.3.2 + 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 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/editor@4.2.18(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/external-editor': 1.0.1(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/expand@4.0.18(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/external-editor@1.0.1(@types/node@24.3.1)': + dependencies: + chardet: 2.1.0 + iconv-lite: 0.6.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/figures@1.0.13': {} + + '@inquirer/input@4.2.2(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/number@3.0.18(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/password@4.0.18(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/prompts@7.8.4(@types/node@24.3.1)': + dependencies: + '@inquirer/checkbox': 4.2.2(@types/node@24.3.1) + '@inquirer/confirm': 5.1.16(@types/node@24.3.1) + '@inquirer/editor': 4.2.18(@types/node@24.3.1) + '@inquirer/expand': 4.0.18(@types/node@24.3.1) + '@inquirer/input': 4.2.2(@types/node@24.3.1) + '@inquirer/number': 3.0.18(@types/node@24.3.1) + '@inquirer/password': 4.0.18(@types/node@24.3.1) + '@inquirer/rawlist': 4.1.6(@types/node@24.3.1) + '@inquirer/search': 3.1.1(@types/node@24.3.1) + '@inquirer/select': 4.3.2(@types/node@24.3.1) + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/rawlist@4.1.6(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/search@3.1.1(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.1) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/select@4.3.2(@types/node@24.3.1)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.1) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.1 + + '@inquirer/type@3.0.8(@types/node@24.3.1)': + optionalDependencies: + '@types/node': 24.3.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jest/diff-sequences@30.0.1': {} + + '@jest/expect-utils@30.1.2': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/get-type@30.1.0': {} + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 20.17.50 + jest-regex-util: 30.0.1 + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.41 + + '@jest/types@30.0.5': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.17.50 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@laynezh/vite-plugin-lib-assets@1.2.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + escape-string-regexp: 4.0.0 + loader-utils: 3.3.1 + mrmime: 1.0.1 + semver: 7.7.2 + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1) + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.0 + optional: true + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@promptbook/utils@0.69.5': + dependencies: + spacetrim: 0.11.59 + + '@puppeteer/browsers@2.10.8': + dependencies: + debug: 4.4.1(supports-color@8.1.1) + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.5.0 + semver: 7.7.2 + tar-fs: 3.1.0 + yargs: 17.7.2 + transitivePeerDependencies: + - bare-buffer + - bare-url + - supports-color + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-inject@5.0.5(rollup@4.50.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.50.1) + estree-walker: 2.0.2 + magic-string: 0.30.19 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/plugin-sucrase@5.0.2(rollup@4.50.1)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.50.1) + sucrase: 3.35.0 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/pluginutils@5.1.4(rollup@4.50.1)': + dependencies: + '@types/estree': 1.0.7 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/rollup-android-arm-eabi@4.50.1': + optional: true + + '@rollup/rollup-android-arm64@4.50.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.50.1': + optional: true + + '@rollup/rollup-darwin-x64@4.50.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.50.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.50.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.50.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.50.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.50.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.50.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.50.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.1': + optional: true + + '@rtsao/scc@1.1.0': + optional: true + + '@sec-ant/readable-stream@0.4.1': {} + + '@sinclair/typebox@0.34.41': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@swc/core-darwin-arm64@1.13.5': + optional: true + + '@swc/core-darwin-x64@1.13.5': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.13.5': + optional: true + + '@swc/core-linux-arm64-gnu@1.13.5': + optional: true + + '@swc/core-linux-arm64-musl@1.13.5': + optional: true + + '@swc/core-linux-x64-gnu@1.13.5': + optional: true + + '@swc/core-linux-x64-musl@1.13.5': + optional: true + + '@swc/core-win32-arm64-msvc@1.13.5': + optional: true + + '@swc/core-win32-ia32-msvc@1.13.5': + optional: true + + '@swc/core-win32-x64-msvc@1.13.5': + optional: true + + '@swc/core@1.13.5': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.13.5 + '@swc/core-darwin-x64': 1.13.5 + '@swc/core-linux-arm-gnueabihf': 1.13.5 + '@swc/core-linux-arm64-gnu': 1.13.5 + '@swc/core-linux-arm64-musl': 1.13.5 + '@swc/core-linux-x64-gnu': 1.13.5 + '@swc/core-linux-x64-musl': 1.13.5 + '@swc/core-win32-arm64-msvc': 1.13.5 + '@swc/core-win32-ia32-msvc': 1.13.5 + '@swc/core-win32-x64-msvc': 1.13.5 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@tybys/wasm-util@0.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chrome@0.0.323': + dependencies: + '@types/filesystem': 0.0.36 + '@types/har-format': 1.2.16 + + '@types/eslint-plugin-jsx-a11y@6.10.0': + dependencies: + '@types/eslint': 9.6.1 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.7': {} + + '@types/estree@1.0.8': {} + + '@types/filesystem@0.0.36': + dependencies: + '@types/filewriter': 0.0.33 + + '@types/filewriter@0.0.33': {} + + '@types/har-format@1.2.16': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': + optional: true + + '@types/mocha@10.0.10': {} + + '@types/node@20.17.50': + dependencies: + undici-types: 6.19.8 + + '@types/node@20.19.13': + dependencies: + undici-types: 6.21.0 + + '@types/node@22.15.21': + dependencies: + undici-types: 6.21.0 + + '@types/node@22.18.1': + dependencies: + undici-types: 6.21.0 + + '@types/node@24.3.1': + dependencies: + undici-types: 7.10.0 + + '@types/normalize-package-data@2.4.4': {} + + '@types/react-dom@19.1.9(@types/react@19.1.12)': + dependencies: + '@types/react': 19.1.12 + + '@types/react@19.1.12': + dependencies: + csstype: 3.1.3 + + '@types/sinonjs__fake-timers@8.1.5': {} + + '@types/stack-utils@2.0.3': {} + + '@types/which@2.0.2': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.15.21 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 24.3.1 + optional: true + + '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/type-utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.43.0 + eslint: 9.35.0(jiti@2.5.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.43.0 + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.35.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.43.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) + '@typescript-eslint/types': 8.43.0 + debug: 4.4.1(supports-color@8.1.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.43.0': + dependencies: + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 + + '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + + '@typescript-eslint/type-utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.35.0(jiti@2.5.1) + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.43.0': {} + + '@typescript-eslint/typescript-estree@8.43.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/project-service': 8.43.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 + debug: 4.4.1(supports-color@8.1.1) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.43.0': + dependencies: + '@typescript-eslint/types': 8.43.0 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vitejs/plugin-react-swc@3.11.0(vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.27 + '@swc/core': 1.13.5 + vite: 6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - '@swc/helpers' + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.19 + pathe: 1.1.2 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@wdio/cli@9.19.2(@types/node@24.3.1)(expect-webdriverio@5.4.2)': + dependencies: + '@vitest/snapshot': 2.1.9 + '@wdio/config': 9.19.2 + '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2) + '@wdio/logger': 9.18.0 + '@wdio/protocols': 9.16.2 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + async-exit-hook: 2.0.1 + chalk: 5.4.1 + chokidar: 4.0.3 + create-wdio: 9.18.2(@types/node@24.3.1) + dotenv: 17.2.2 + import-meta-resolve: 4.1.0 + lodash.flattendeep: 4.4.0 + lodash.pickby: 4.6.0 + lodash.union: 4.6.0 + read-pkg-up: 10.1.0 + tsx: 4.20.5 + webdriverio: 9.19.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - bare-buffer + - bare-url + - bufferutil + - expect-webdriverio + - puppeteer-core + - supports-color + - utf-8-validate + + '@wdio/config@9.19.2': + dependencies: + '@wdio/logger': 9.18.0 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + deepmerge-ts: 7.1.5 + glob: 10.4.5 + import-meta-resolve: 4.1.0 + transitivePeerDependencies: + - bare-buffer + - bare-url + - supports-color + + '@wdio/dot-reporter@9.19.2': + dependencies: + '@wdio/reporter': 9.19.2 + '@wdio/types': 9.19.2 + chalk: 5.6.2 + + '@wdio/globals@9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2)': + dependencies: + expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2) + webdriverio: 9.19.2 + + '@wdio/local-runner@9.19.2(@wdio/globals@9.17.0)(webdriverio@9.19.2)': + dependencies: + '@types/node': 20.17.50 + '@wdio/logger': 9.18.0 + '@wdio/repl': 9.16.2 + '@wdio/runner': 9.19.2(expect-webdriverio@5.4.2)(webdriverio@9.19.2) + '@wdio/types': 9.19.2 + '@wdio/xvfb': 9.19.2 + exit-hook: 4.0.0 + expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2) + split2: 4.2.0 + stream-buffers: 3.0.3 + transitivePeerDependencies: + - '@wdio/globals' + - bare-buffer + - bare-url + - bufferutil + - supports-color + - utf-8-validate + - webdriverio + + '@wdio/logger@9.18.0': + dependencies: + chalk: 5.4.1 + loglevel: 1.9.2 + loglevel-plugin-prefix: 0.8.4 + safe-regex2: 5.0.0 + strip-ansi: 7.1.2 + + '@wdio/mocha-framework@9.19.2': + dependencies: + '@types/mocha': 10.0.10 + '@types/node': 20.17.50 + '@wdio/logger': 9.18.0 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + mocha: 10.8.2 + transitivePeerDependencies: + - bare-buffer + - bare-url + - supports-color + + '@wdio/protocols@9.16.2': {} + + '@wdio/repl@9.16.2': + dependencies: + '@types/node': 20.17.50 + + '@wdio/reporter@9.19.2': + dependencies: + '@types/node': 20.19.13 + '@wdio/logger': 9.18.0 + '@wdio/types': 9.19.2 + diff: 8.0.2 + object-inspect: 1.13.4 + + '@wdio/runner@9.19.2(expect-webdriverio@5.4.2)(webdriverio@9.19.2)': + dependencies: + '@types/node': 20.17.50 + '@wdio/config': 9.19.2 + '@wdio/dot-reporter': 9.19.2 + '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2) + '@wdio/logger': 9.18.0 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + deepmerge-ts: 7.1.5 + expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2) + webdriver: 9.19.2 + webdriverio: 9.19.2 + transitivePeerDependencies: + - bare-buffer + - bare-url + - bufferutil + - supports-color + - utf-8-validate + + '@wdio/spec-reporter@9.19.2': + dependencies: + '@wdio/reporter': 9.19.2 + '@wdio/types': 9.19.2 + chalk: 5.4.1 + easy-table: 1.2.0 + pretty-ms: 9.2.0 + + '@wdio/types@9.19.2': + dependencies: + '@types/node': 20.17.50 + + '@wdio/utils@9.19.2': + dependencies: + '@puppeteer/browsers': 2.10.8 + '@wdio/logger': 9.18.0 + '@wdio/types': 9.19.2 + decamelize: 6.0.1 + deepmerge-ts: 7.1.5 + edgedriver: 6.1.2 + geckodriver: 5.0.0 + get-port: 7.1.0 + import-meta-resolve: 4.1.0 + locate-app: 2.5.0 + mitt: 3.0.1 + safaridriver: 1.0.0 + split2: 4.2.0 + wait-port: 1.1.0 + transitivePeerDependencies: + - bare-buffer + - bare-url + - supports-color + + '@wdio/xvfb@9.19.2': + dependencies: + '@wdio/logger': 9.18.0 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + 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 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + 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 + + '@webassemblyjs/wasm-gen@1.14.1': + 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 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + 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 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@zip.js/zip.js@2.8.2': {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.1.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + archiver-utils@5.0.2: + dependencies: + glob: 10.4.5 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + optional: true + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + optional: true + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.2 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + assert@2.1.0: + dependencies: + call-bind: 1.0.8 + is-nan: 1.3.2 + object-is: 1.1.6 + object.assign: 4.1.7 + util: 0.12.5 + + ast-types-flow@0.0.8: {} + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + + async-exit-hook@2.0.1: {} + + async-function@1.0.0: {} + + async@3.2.6: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.24.5 + caniuse-lite: 1.0.30001718 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.10.3: {} + + axobject-query@4.1.0: {} + + b4a@1.6.7: {} + + balanced-match@1.0.2: {} + + bare-events@2.6.1: + optional: true + + bare-fs@4.3.2: + dependencies: + bare-events: 2.6.1 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.6.1) + optional: true + + bare-os@3.6.2: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.2 + optional: true + + bare-stream@2.7.0(bare-events@2.6.1): + dependencies: + streamx: 2.22.1 + optionalDependencies: + bare-events: 2.6.1 + optional: true + + base64-js@1.5.1: {} + + basic-ftp@5.0.5: {} + + binary-extensions@2.3.0: {} + + bn.js@4.12.2: {} + + bn.js@5.2.2: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browser-resolve@2.0.0: + dependencies: + resolve: 1.22.10 + + browser-stdout@1.3.1: {} + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.6 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.6 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.1: + dependencies: + bn.js: 5.2.2 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + browserify-sign@4.2.3: + dependencies: + bn.js: 5.2.2 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.6.1 + hash-base: 3.0.5 + inherits: 2.0.4 + parse-asn1: 5.1.7 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserslist@4.24.5: + dependencies: + caniuse-lite: 1.0.30001718 + electron-to-chromium: 1.5.155 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.5) + + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.215 + node-releases: 2.0.20 + update-browserslist-db: 1.1.3(browserslist@4.25.4) + + buffer-crc32@0.2.13: {} + + buffer-crc32@1.0.0: {} + + buffer-from@1.1.2: {} + + buffer-xor@1.0.3: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builtin-status-codes@3.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001718: {} + + caniuse-lite@1.0.30001741: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chalk@5.6.2: {} + + chardet@2.1.0: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.1.2: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.0.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.16.0 + whatwg-mimetype: 4.0.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chrome-trace-event@1.0.4: {} + + ci-info@4.3.0: {} + + cipher-base@1.0.6: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cli-width@4.1.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: + optional: true + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + commander@11.1.0: {} + + commander@14.0.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@9.5.0: {} + + comment-parser@1.4.1: {} + + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + concat-map@0.0.1: {} + + console-browserify@1.2.0: {} + + constants-browserify@1.0.0: {} + + core-util-is@1.0.3: {} + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.2 + elliptic: 6.6.1 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.6 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.6 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + + create-require@1.1.1: {} + + create-wdio@9.18.2(@types/node@24.3.1): + dependencies: + chalk: 5.4.1 + commander: 14.0.0 + cross-spawn: 7.0.6 + ejs: 3.1.10 + execa: 9.6.0 + import-meta-resolve: 4.1.0 + inquirer: 12.9.4(@types/node@24.3.1) + normalize-package-data: 7.0.1 + read-pkg-up: 10.1.0 + recursive-readdir: 2.2.3 + semver: 7.7.2 + type-fest: 4.41.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-browserify@3.12.1: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.3 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + hash-base: 3.0.5 + inherits: 2.0.4 + pbkdf2: 3.1.2 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-shorthand-properties@1.1.2: {} + + css-value@0.0.1: {} + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csstype@3.1.3: {} + + damerau-levenshtein@1.0.8: {} + + data-uri-to-buffer@4.0.1: {} + + data-uri-to-buffer@6.0.2: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + optional: true + + debug@4.4.1(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decamelize@4.0.0: {} + + decamelize@6.0.1: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + deepmerge-ts@7.1.5: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + optional: true + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + detect-libc@1.0.3: + optional: true + + didyoumean@1.2.2: {} + + diff@4.0.2: {} + + diff@5.2.0: {} + + diff@8.0.2: {} + + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.2 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domain-browser@4.22.0: {} + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv@17.2.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + easy-table@1.2.0: + dependencies: + ansi-regex: 5.0.1 + optionalDependencies: + wcwidth: 1.0.1 + + eciesjs@0.4.15: + dependencies: + '@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + + edge-paths@3.0.5: + dependencies: + '@types/which': 2.0.2 + which: 2.0.2 + + edgedriver@6.1.2: + dependencies: + '@wdio/logger': 9.18.0 + '@zip.js/zip.js': 2.8.2 + decamelize: 6.0.1 + edge-paths: 3.0.5 + fast-xml-parser: 5.2.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.155: {} + + electron-to-chromium@1.5.215: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.2 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + emoji-regex@10.5.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + + entities@4.5.0: {} + + entities@6.0.1: {} + + environment@1.1.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.23.9: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + optional: true + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)): + dependencies: + eslint: 9.35.0(jiti@2.5.1) + + eslint-import-context@0.1.9(unrs-resolver@1.11.1): + dependencies: + get-tsconfig: 4.10.1 + stable-hash-x: 0.2.0 + optionalDependencies: + unrs-resolver: 1.11.1 + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + optional: true + + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)): + dependencies: + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.35.0(jiti@2.5.1) + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) + get-tsconfig: 4.10.1 + is-bun-module: 2.0.0 + stable-hash-x: 0.2.0 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.31.0)(eslint@9.35.0(jiti@2.5.1)) + transitivePeerDependencies: + - supports-color + optional: true + + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)): + dependencies: + '@typescript-eslint/types': 8.43.0 + comment-parser: 1.4.1 + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.35.0(jiti@2.5.1) + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) + is-glob: 4.0.3 + minimatch: 10.0.1 + semver: 7.7.2 + stable-hash-x: 0.2.0 + unrs-resolver: 1.11.1 + optionalDependencies: + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.35.0(jiti@2.5.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + optional: true + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.35.0(jiti@2.5.1)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.10.3 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.35.0(jiti@2.5.1) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2): + dependencies: + eslint: 9.35.0(jiti@2.5.1) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + '@types/eslint': 9.6.1 + eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + + eslint-plugin-react-hooks@5.2.0(eslint@9.35.0(jiti@2.5.1)): + dependencies: + eslint: 9.35.0(jiti@2.5.1) + + eslint-plugin-react@7.37.5(eslint@9.35.0(jiti@2.5.1)): + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.35.0(jiti@2.5.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2))): + dependencies: + fast-glob: 3.3.3 + postcss: 8.5.6 + tailwindcss: 3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2)) + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.35.0(jiti@2.5.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.35.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.5.1 + transitivePeerDependencies: + - supports-color + + esm@3.2.25: {} + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + exit-hook@4.0.0: {} + + expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2): + dependencies: + '@vitest/snapshot': 3.2.4 + '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2) + '@wdio/logger': 9.18.0 + deep-eql: 5.0.2 + expect: 30.1.2 + jest-matcher-utils: 30.1.2 + webdriverio: 9.19.2 + + expect@30.1.2: + dependencies: + '@jest/expect-utils': 30.1.2 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.1.2 + jest-message-util: 30.1.0 + jest-mock: 30.0.5 + jest-util: 30.0.5 + + extract-zip@2.0.1: + dependencies: + debug: 4.4.1(supports-color@8.1.1) + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@2.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.1 + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + fflate@0.8.2: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + fraction.js@4.3.7: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + geckodriver@5.0.0: + dependencies: + '@wdio/logger': 9.18.0 + '@zip.js/zip.js': 2.8.2 + decamelize: 6.0.1 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + tar-fs: 3.1.0 + which: 5.0.0 + transitivePeerDependencies: + - bare-buffer + - bare-url + - supports-color + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + 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 + + get-port@7.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + + get-stream@6.0.1: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-uri@6.0.5: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.0.2: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.0 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + grapheme-splitter@1.0.4: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash-base@3.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + + htmlfy@0.8.1: {} + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-browserify@1.0.0: {} + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + human-signals@8.0.1: {} + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + immutable@5.1.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.1.0: {} + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inquirer@12.9.4(@types/node@24.3.1): + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.1) + '@inquirer/prompts': 7.8.4(@types/node@24.3.1) + '@inquirer/type': 3.0.8(@types/node@24.3.1) + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 4.0.6 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 24.3.1 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + ip-address@10.0.1: {} + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-nan@1.3.2: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + is-negative-zero@2.0.3: + optional: true + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@4.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-stream@4.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + isomorphic-timers-promises@1.0.1: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.0: + dependencies: + '@isaacs/cliui': 8.0.2 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + + jest-diff@30.1.2: + dependencies: + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.0.5 + + jest-matcher-utils@30.1.2: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.1.2 + pretty-format: 30.0.5 + + jest-message-util@30.1.0: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 30.0.5 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 30.0.5 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@30.0.5: + dependencies: + '@jest/types': 30.0.5 + '@types/node': 20.17.50 + jest-util: 30.0.5 + + jest-regex-util@30.0.1: {} + + jest-util@30.0.5: + dependencies: + '@jest/types': 30.0.5 + '@types/node': 20.17.50 + chalk: 4.1.2 + ci-info: 4.3.0 + graceful-fs: 4.2.11 + picomatch: 4.0.3 + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.18.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jiti@1.21.7: {} + + jiti@2.5.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-parse-even-better-errors@3.0.2: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + optional: true + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lines-and-columns@2.0.4: {} + + lint-staged@16.1.6: + dependencies: + chalk: 5.6.2 + commander: 14.0.0 + debug: 4.4.1(supports-color@8.1.1) + lilconfig: 3.1.3 + listr2: 9.0.3 + micromatch: 4.0.8 + nano-spawn: 1.0.2 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.1 + transitivePeerDependencies: + - supports-color + + listr2@9.0.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + loader-runner@4.3.0: {} + + loader-utils@3.3.1: {} + + locate-app@2.5.0: + dependencies: + '@promptbook/utils': 0.69.5 + type-fest: 4.26.0 + userhome: 1.0.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.clonedeep@4.5.0: {} + + lodash.flattendeep@4.4.0: {} + + lodash.merge@4.6.2: {} + + lodash.pickby@4.6.0: {} + + lodash.union@4.6.0: {} + + lodash.zip@4.2.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.1.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + + loglevel-plugin-prefix@0.8.4: {} + + loglevel@1.9.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.4.3: {} + + lru-cache@11.1.0: {} + + lru-cache@7.18.3: {} + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-error@1.3.6: {} + + math-intrinsics@1.1.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.2 + brorand: 1.1.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: + optional: true + + minipass@7.1.2: {} + + mitt@3.0.1: {} + + mocha@10.8.2: + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.4.1(supports-color@8.1.1) + diff: 5.2.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.1.6 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + + mrmime@1.0.1: {} + + ms@2.1.3: {} + + mute-stream@2.0.0: {} + + mylas@2.1.13: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.3: {} + + natural-compare@1.4.0: {} + + neo-async@2.6.2: {} + + netmask@2.0.2: {} + + node-addon-api@7.1.1: + optional: true + + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-releases@2.0.19: {} + + node-releases@2.0.20: {} + + node-stdlib-browser@1.3.1: + dependencies: + assert: 2.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + create-require: 1.1.1 + crypto-browserify: 3.12.1 + domain-browser: 4.22.0 + events: 3.3.0 + https-browserify: 1.0.0 + isomorphic-timers-promises: 1.0.1 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + pkg-dir: 5.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 3.6.2 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@7.0.1: + dependencies: + hosted-git-info: 8.1.0 + semver: 7.7.2 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object-treeify@1.1.33: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + optional: true + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + os-browserify@0.3.0: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.1(supports-color@8.1.1) + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + + package-json-from-dist@1.0.1: {} + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-asn1@5.1.7: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + hash-base: 3.0.5 + pbkdf2: 3.1.2 + safe-buffer: 5.2.1 + + parse-json@7.1.1: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 3.0.2 + lines-and-columns: 2.0.4 + type-fest: 3.13.1 + + parse-ms@4.0.0: {} + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.1.0 + minipass: 7.1.2 + + path-type@4.0.0: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + pbkdf2@3.1.2: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + + pend@1.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pify@2.3.0: {} + + pirates@4.0.7: {} + + pkg-dir@5.0.0: + dependencies: + find-up: 5.0.0 + + plimit-lit@1.6.1: + dependencies: + queue-lit: 1.5.2 + + possible-typed-array-names@1.1.0: {} + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-js@4.0.1(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2)): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.1 + optionalDependencies: + postcss: 8.5.6 + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2) + + postcss-load-config@6.0.1(jiti@2.5.1)(postcss@8.5.6)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 2.5.1 + postcss: 8.5.6 + tsx: 4.20.5 + yaml: 2.8.1 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-tailwindcss@0.6.14(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + + prettier@3.6.2: {} + + pretty-format@30.0.5: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + progress@2.0.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@8.1.1) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + proxy-from-env@1.1.0: {} + + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.2 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + parse-asn1: 5.1.7 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@1.4.1: {} + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + query-selector-shadow-dom@1.0.1: {} + + querystring-es3@0.2.1: {} + + queue-lit@1.5.2: {} + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + + react-error-boundary@6.0.0(react@19.1.1): + dependencies: + '@babel/runtime': 7.27.1 + react: 19.1.1 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-spinners@0.17.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + react@19.1.1: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-pkg-up@10.1.0: + dependencies: + find-up: 6.3.0 + read-pkg: 8.1.0 + type-fest: 4.41.0 + + read-pkg@8.1.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 6.0.2 + parse-json: 7.1.1 + type-fest: 4.41.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + recursive-readdir@2.2.3: + dependencies: + minimatch: 3.1.2 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resq@1.11.0: + dependencies: + fast-deep-equal: 2.0.1 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + ret@0.5.0: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rgb2hex@0.2.5: {} + + rimraf@6.0.1: + dependencies: + glob: 11.0.2 + package-json-from-dist: 1.0.1 + + ripemd160@2.0.2: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + + rollup@4.50.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 + fsevents: 2.3.3 + + run-async@4.0.6: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + run-script-os@1.1.6: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safaridriver@1.0.0: {} + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-regex2@5.0.0: + dependencies: + ret: 0.5.0 + + safer-buffer@2.1.2: {} + + sass@1.92.1: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.2 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + scheduler@0.26.0: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + + schema-utils@4.3.2: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) + + semver@6.3.1: {} + + semver@7.7.2: {} + + serialize-error@12.0.0: + dependencies: + type-fest: 4.41.0 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setimmediate@1.0.5: {} + + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@8.1.1) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + spacetrim@0.11.59: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + split2@4.2.0: {} + + stable-hash-x@0.2.0: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + optional: true + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-buffers@3.0.3: {} + + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + streamx@2.22.1: + dependencies: + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.6.1 + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.5.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.9 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: + optional: true + + strip-final-newline@2.0.0: {} + + strip-final-newline@4.0.0: {} + + strip-json-comments@3.1.1: {} + + strnum@2.1.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + tailwind-merge@3.3.1: {} + + tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2)): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2)) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tapable@2.2.2: {} + + tapable@2.2.3: {} + + tar-fs@3.1.0: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.3.2 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-buffer + - bare-url + + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.22.1 + + terser-webpack-plugin@5.3.14(@swc/core@1.13.5)(webpack@5.94.0(@swc/core@1.13.5)): + dependencies: + '@jridgewell/trace-mapping': 0.3.30 + jest-worker: 27.5.1 + schema-utils: 4.3.2 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.94.0(@swc/core@1.13.5) + optionalDependencies: + '@swc/core': 1.13.5 + + terser@5.44.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@1.2.0: {} + + tinyrainbow@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.1.0(typescript@5.9.2): + dependencies: + typescript: 5.9.2 + + ts-interface-checker@0.1.13: {} + + ts-loader@9.5.4(typescript@5.9.2)(webpack@5.94.0(@swc/core@1.13.5)): + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.18.1 + micromatch: 4.0.8 + semver: 7.7.2 + source-map: 0.7.4 + typescript: 5.9.2 + webpack: 5.94.0(@swc/core@1.13.5) + + ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.1)(typescript@5.9.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.18.1 + acorn: 8.14.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.9.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.13.5 + optional: true + + ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.3.1)(typescript@5.9.2): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.3.1 + acorn: 8.14.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.9.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.13.5 + + tsc-alias@1.8.16: + dependencies: + chokidar: 3.6.0 + commander: 9.5.0 + get-tsconfig: 4.10.1 + globby: 11.1.0 + mylas: 2.1.13 + normalize-path: 3.0.0 + plimit-lit: 1.6.1 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + optional: true + + tslib@2.8.1: {} + + tsx@4.20.5: + dependencies: + esbuild: 0.25.9 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + tty-browserify@0.0.1: {} + + turbo-darwin-64@2.5.6: + optional: true + + turbo-darwin-arm64@2.5.6: + optional: true + + turbo-linux-64@2.5.6: + optional: true + + turbo-linux-arm64@2.5.6: + optional: true + + turbo-windows-64@2.5.6: + optional: true + + turbo-windows-arm64@2.5.6: + optional: true + + turbo@2.5.6: + optionalDependencies: + turbo-darwin-64: 2.5.6 + turbo-darwin-arm64: 2.5.6 + turbo-linux-64: 2.5.6 + turbo-linux-arm64: 2.5.6 + turbo-windows-64: 2.5.6 + turbo-windows-arm64: 2.5.6 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.21.3: {} + + type-fest@3.13.1: {} + + type-fest@4.26.0: {} + + type-fest@4.41.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + typescript@5.9.2: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.19.8: {} + + undici-types@6.21.0: {} + + undici-types@7.10.0: {} + + undici@6.21.3: {} + + undici@7.16.0: {} + + unicorn-magic@0.3.0: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.3 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.1.3(browserslist@4.24.5): + dependencies: + browserslist: 4.24.5 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.14.0 + + urlpattern-polyfill@10.1.0: {} + + userhome@1.0.1: {} + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.0 + is-typed-array: 1.1.15 + which-typed-array: 1.1.19 + + v8-compile-cache-lib@3.0.1: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + vite-plugin-node-polyfills@0.23.0(rollup@4.50.1)(vite@6.3.6(@types/node@22.18.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)): + dependencies: + '@rollup/plugin-inject': 5.0.5(rollup@4.50.1) + node-stdlib-browser: 1.3.1 + vite: 6.3.6(@types/node@22.18.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1) + transitivePeerDependencies: + - rollup + + vite@6.3.6(@types/node@22.18.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.1 + fsevents: 2.3.3 + jiti: 2.5.1 + sass: 1.92.1 + terser: 5.44.0 + tsx: 4.20.5 + yaml: 2.8.1 + + vite@6.3.6(@types/node@24.3.1)(jiti@2.5.1)(sass@1.92.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.3.1 + fsevents: 2.3.3 + jiti: 2.5.1 + sass: 1.92.1 + terser: 5.44.0 + tsx: 4.20.5 + yaml: 2.8.1 + + vm-browserify@1.1.2: {} + + wait-port@1.1.0: + dependencies: + chalk: 4.1.2 + commander: 9.5.0 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + watchpack@2.4.4: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + optional: true + + web-streams-polyfill@3.3.3: {} + + webdriver@9.19.2: + dependencies: + '@types/node': 20.19.13 + '@types/ws': 8.18.1 + '@wdio/config': 9.19.2 + '@wdio/logger': 9.18.0 + '@wdio/protocols': 9.16.2 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + deepmerge-ts: 7.1.5 + https-proxy-agent: 7.0.6 + undici: 6.21.3 + ws: 8.18.3 + transitivePeerDependencies: + - bare-buffer + - bare-url + - bufferutil + - supports-color + - utf-8-validate + + webdriverio@9.19.2: + dependencies: + '@types/node': 20.19.13 + '@types/sinonjs__fake-timers': 8.1.5 + '@wdio/config': 9.19.2 + '@wdio/logger': 9.18.0 + '@wdio/protocols': 9.16.2 + '@wdio/repl': 9.16.2 + '@wdio/types': 9.19.2 + '@wdio/utils': 9.19.2 + archiver: 7.0.1 + aria-query: 5.3.2 + cheerio: 1.1.2 + css-shorthand-properties: 1.1.2 + css-value: 0.0.1 + grapheme-splitter: 1.0.4 + htmlfy: 0.8.1 + is-plain-obj: 4.1.0 + jszip: 3.10.1 + lodash.clonedeep: 4.5.0 + lodash.zip: 4.2.0 + query-selector-shadow-dom: 1.0.1 + resq: 1.11.0 + rgb2hex: 0.2.5 + serialize-error: 12.0.0 + urlpattern-polyfill: 10.1.0 + webdriver: 9.19.2 + transitivePeerDependencies: + - bare-buffer + - bare-url + - bufferutil + - supports-color + - utf-8-validate + + webextension-polyfill@0.12.0: {} + + webpack-sources@3.3.3: {} + + webpack@5.94.0(@swc/core@1.13.5): + dependencies: + '@types/estree': 1.0.8 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + browserslist: 4.25.4 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.3 + terser-webpack-plugin: 5.3.14(@swc/core@1.13.5)(webpack@5.94.0(@swc/core@1.13.5)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@4.0.0: + dependencies: + isexe: 3.1.1 + + which@5.0.0: + dependencies: + isexe: 3.1.1 + + word-wrap@1.2.5: {} + + workerpool@6.5.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + ws@8.18.3: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yaml@2.8.1: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.1: {} + + yoctocolors-cjs@2.1.3: {} + + yoctocolors@2.1.2: {} + + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9d5a113 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,13 @@ +packages: + - chrome-extension + - pages/* + - packages/* + - tests/* + +onlyBuiltDependencies: + - '@parcel/watcher' + - '@swc/core' + - edgedriver + - esbuild + - geckodriver + - unrs-resolver diff --git a/tests/e2e/config/wdio.browser.conf.ts b/tests/e2e/config/wdio.browser.conf.ts new file mode 100644 index 0000000..bda8167 --- /dev/null +++ b/tests/e2e/config/wdio.browser.conf.ts @@ -0,0 +1,58 @@ +import { config as baseConfig } from './wdio.conf.js'; +import { getChromeExtensionPath, getFirefoxExtensionPath } from '../utils/extension-path.js'; +import { IS_CI, IS_FIREFOX } from '@extension/env'; +import { readdir, readFile } from 'node:fs/promises'; +import { extname, join } from 'node:path'; + +const extName = IS_FIREFOX ? '.xpi' : '.zip'; +const extensions = await readdir(join(import.meta.dirname, '../../../dist-zip')); +const latestExtension = extensions.filter(file => extname(file) === extName).at(-1); +const extPath = join(import.meta.dirname, `../../../dist-zip/${latestExtension}`); +const bundledExtension = (await readFile(extPath)).toString('base64'); + +const chromeCapabilities = { + browserName: 'chrome', + acceptInsecureCerts: true, + 'goog:chromeOptions': { + args: [ + '--disable-web-security', + '--disable-gpu', + '--no-sandbox', + '--disable-dev-shm-usage', + ...(IS_CI ? ['--headless'] : []), + ], + prefs: { 'extensions.ui.developer_mode': true }, + extensions: [bundledExtension], + }, +}; + +const firefoxCapabilities = { + browserName: 'firefox', + acceptInsecureCerts: true, + 'moz:firefoxOptions': { + args: [...(IS_CI ? ['--headless'] : [])], + }, +}; + +export const config: WebdriverIO.Config = { + ...baseConfig, + capabilities: IS_FIREFOX ? [firefoxCapabilities] : [chromeCapabilities], + + maxInstances: IS_CI ? 10 : 1, + logLevel: 'error', + execArgv: IS_CI ? [] : ['--inspect'], + before: async ({ browserName }: WebdriverIO.Capabilities, _specs, browser: WebdriverIO.Browser) => { + if (browserName === 'firefox') { + await browser.installAddOn(bundledExtension, true); + + browser.addCommand('getExtensionPath', async () => getFirefoxExtensionPath(browser)); + } else if (browserName === 'chrome') { + browser.addCommand('getExtensionPath', async () => getChromeExtensionPath(browser)); + } + }, + afterTest: async () => { + if (!IS_CI) { + await browser.pause(500); + } + }, +}; diff --git a/tests/e2e/config/wdio.conf.ts b/tests/e2e/config/wdio.conf.ts new file mode 100644 index 0000000..2567bb6 --- /dev/null +++ b/tests/e2e/config/wdio.conf.ts @@ -0,0 +1,97 @@ +/** + * WebdriverIO v9 configuration file + * https://webdriver.io/docs/configurationfile + */ +export const config: WebdriverIO.Config = { + runner: 'local', + tsConfigPath: '../tsconfig.json', + + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // of the configuration file being run. + // + // The specs are defined as an array of spec files (optionally using wildcards + // that will be expanded). The test for each spec file will be run in a separate + // worker process. In order to have a group of spec files run in the same worker + // process simply enclose them in an array within the specs array. + // + // The path of the spec files will be resolved relative from the directory + // of the config file unless it's absolute. + specs: ['../specs/**/*.ts'], + // Patterns to exclude. + exclude: [], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10, all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + maxInstances: 10, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://saucelabs.com/platform/platform-configurator + // + capabilities: [], + + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'info', + + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 120000, + + // + // Default request retries count + connectionRetryCount: 3, + + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'mocha', + + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter + reporters: ['spec'], + + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + timeout: 60000, + }, +}; diff --git a/tests/e2e/config/wdio.d.ts b/tests/e2e/config/wdio.d.ts new file mode 100644 index 0000000..8dbebe4 --- /dev/null +++ b/tests/e2e/config/wdio.d.ts @@ -0,0 +1,7 @@ +declare namespace WebdriverIO { + interface Browser extends WebdriverIO.Browser { + getExtensionPath: () => Promise; + installAddOn: (extension: string, temporary: boolean) => Promise; + addCommand: (name: string, func: () => Promise) => void; + } +} diff --git a/tests/e2e/helpers/theme.ts b/tests/e2e/helpers/theme.ts new file mode 100644 index 0000000..9dfd6c1 --- /dev/null +++ b/tests/e2e/helpers/theme.ts @@ -0,0 +1,26 @@ +/** + * Helper method to check if user can click on theme button and toggle theme color + */ +export const canSwitchTheme = async () => { + const LIGHT_THEME_CLASS = 'bg-slate-50'; + const DARK_THEME_CLASS = 'bg-gray-800'; + const TOGGLE_BUTTON_TEXT = 'Toggle theme'; + + const app = await $('.App').getElement(); + const toggleThemeButton = await $(`button=${TOGGLE_BUTTON_TEXT}`).getElement(); + + await expect(app).toBeExisting(); + await expect(toggleThemeButton).toBeExisting(); + + const appClasses = await app.getAttribute('class'); + const initialThemeClass = appClasses.includes(LIGHT_THEME_CLASS) ? LIGHT_THEME_CLASS : DARK_THEME_CLASS; + const afterClickThemeClass = appClasses.includes(LIGHT_THEME_CLASS) ? DARK_THEME_CLASS : LIGHT_THEME_CLASS; + + // Toggle theme + await toggleThemeButton.click(); + await expect(app).toHaveElementClass(afterClickThemeClass); + + // Toggle back to initial theme + await toggleThemeButton.click(); + await expect(app).toHaveElementClass(initialThemeClass); +}; diff --git a/tests/e2e/package.json b/tests/e2e/package.json new file mode 100644 index 0000000..51874fb --- /dev/null +++ b/tests/e2e/package.json @@ -0,0 +1,24 @@ +{ + "name": "@extension/e2e", + "version": "0.5.0", + "description": "E2e tests configuration boilerplate", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "e2e": "wdio run config/wdio.browser.conf.ts", + "clean:node_modules": "pnpx rimraf node_modules", + "clean:turbo": "pnpx rimraf .turbo", + "clean": "pnpm clean:turbo && pnpm clean:node_modules" + }, + "devDependencies": { + "@extension/env": "workspace:*", + "@extension/tsconfig": "workspace:*", + "@wdio/cli": "^9.14.0", + "@wdio/globals": "^9.14.0", + "@wdio/local-runner": "^9.14.0", + "@wdio/mocha-framework": "^9.14.0", + "@wdio/spec-reporter": "^9.14.0", + "@wdio/types": "^9.14.0" + } +} diff --git a/tests/e2e/specs/page-content-runtime.test.ts b/tests/e2e/specs/page-content-runtime.test.ts new file mode 100644 index 0000000..6a4bc7f --- /dev/null +++ b/tests/e2e/specs/page-content-runtime.test.ts @@ -0,0 +1,36 @@ +describe('Webextension Content Runtime Script', () => { + before(function () { + // Chrome doesn't allow content scripts on the extension pages + if ((browser.capabilities as WebdriverIO.Capabilities).browserName === 'chrome') { + this.skip(); + } + }); + + it('should create all runtime elements on the page', async function () { + // Open the popup + const extensionPath = await browser.getExtensionPath(); + const popupUrl = `${extensionPath}/popup/index.html`; + + // if Popup file not found, skip the test + try { + await browser.url(popupUrl); + } catch { + console.error('Popup file not found'); + this.skip(); + } + + await expect(browser).toHaveTitle('Popup'); + + // Trigger inject button on popup + const contentScriptsButton = await $('button*=Content Scripts').getElement(); + + await contentScriptsButton.click(); + + // Check if id exists on the page + const runtimeExampleElement = await $('#CEB-extension-runtime-example').getElement(); + const runtimeAllElement = await $('#CEB-extension-runtime-all').getElement(); + + await expect(runtimeExampleElement).toBeExisting(); + await expect(runtimeAllElement).toBeExisting(); + }); +}); diff --git a/tests/e2e/specs/page-content-ui.test.ts b/tests/e2e/specs/page-content-ui.test.ts new file mode 100644 index 0000000..0ce4cda --- /dev/null +++ b/tests/e2e/specs/page-content-ui.test.ts @@ -0,0 +1,21 @@ +describe('Content UI Injection', () => { + it('should locate the injected content UI (all and example) div on example.com`', async () => { + await browser.url('https://example.com'); + + const contentAllDiv = await $('#CEB-extension-all').getElement(); + await expect(contentAllDiv).toBeDisplayed(); + + const contentExampleDiv = await $('#CEB-extension-example').getElement(); + await expect(contentExampleDiv).toBeDisplayed(); + }); + + it('should locate the injected content UI all div and not locate example div on google.com', async () => { + await browser.url('https://www.google.com'); + + const contentAllDiv = await $('#CEB-extension-all').getElement(); + await expect(contentAllDiv).toBeDisplayed(); + + const contentExampleDiv = await $('#CEB-extension-example').getElement(); + await expect(contentExampleDiv).not.toBeDisplayed(); + }); +}); diff --git a/tests/e2e/specs/page-content.test.ts b/tests/e2e/specs/page-content.test.ts new file mode 100644 index 0000000..3bd8c67 --- /dev/null +++ b/tests/e2e/specs/page-content.test.ts @@ -0,0 +1,33 @@ +describe('Webextension Content Script', () => { + it('should log "example content script loaded" in console on example.com', async () => { + await browser.sessionSubscribe({ events: ['log.entryAdded'] }); + const logs: (string | null)[] = []; + + browser.on('log.entryAdded', logEntry => { + logs.push(logEntry.text); + }); + + await browser.url('https://example.com'); + + const EXPECTED_LOG_MESSAGE = '[CEB] Example content script loaded'; + await browser.waitUntil(() => logs.includes(EXPECTED_LOG_MESSAGE)); + + expect(logs).toContain(EXPECTED_LOG_MESSAGE); + }); + + it('should log "all content script loaded" in console on any page', async () => { + await browser.sessionSubscribe({ events: ['log.entryAdded'] }); + const logs: (string | null)[] = []; + + browser.on('log.entryAdded', logEntry => { + logs.push(logEntry.text); + }); + + await browser.url('https://www.google.com'); + + const EXPECTED_LOG_MESSAGE = '[CEB] All content script loaded'; + await browser.waitUntil(() => logs.includes(EXPECTED_LOG_MESSAGE)); + + expect(logs).toContain(EXPECTED_LOG_MESSAGE); + }); +}); diff --git a/tests/e2e/specs/page-devtools-panel.test.ts b/tests/e2e/specs/page-devtools-panel.test.ts new file mode 100644 index 0000000..5284f7f --- /dev/null +++ b/tests/e2e/specs/page-devtools-panel.test.ts @@ -0,0 +1,12 @@ +import { canSwitchTheme } from '../helpers/theme.js'; + +describe('Webextension DevTools Panel', () => { + it('should make DevTools panel available', async () => { + const extensionPath = await browser.getExtensionPath(); + const devtoolsPanelUrl = `${extensionPath}/devtools-panel/index.html`; + + await browser.url(devtoolsPanelUrl); + await expect(browser).toHaveTitle('Devtools Panel'); + await canSwitchTheme(); + }); +}); diff --git a/tests/e2e/specs/page-new-tab.test.ts b/tests/e2e/specs/page-new-tab.test.ts new file mode 100644 index 0000000..44501c0 --- /dev/null +++ b/tests/e2e/specs/page-new-tab.test.ts @@ -0,0 +1,15 @@ +import { canSwitchTheme } from '../helpers/theme.js'; + +describe('Webextension New Tab', () => { + it('should open the extension page when a new tab is opened', async () => { + const extensionPath = await browser.getExtensionPath(); + const newTabUrl = + process.env.CLI_CEB_FIREFOX === 'true' ? `${extensionPath}/new-tab/index.html` : 'chrome://newtab'; + + await browser.url(newTabUrl); + + const appDiv = await $('.App').getElement(); + await expect(appDiv).toBeExisting(); + await canSwitchTheme(); + }); +}); diff --git a/tests/e2e/specs/page-options.test.ts b/tests/e2e/specs/page-options.test.ts new file mode 100644 index 0000000..8dd308e --- /dev/null +++ b/tests/e2e/specs/page-options.test.ts @@ -0,0 +1,13 @@ +import { canSwitchTheme } from '../helpers/theme.js'; + +describe('Webextension Options Page', () => { + it('should make options page accessible', async () => { + const extensionPath = await browser.getExtensionPath(); + const optionsUrl = `${extensionPath}/options/index.html`; + + await browser.url(optionsUrl); + + await expect(browser).toHaveTitle('Options'); + await canSwitchTheme(); + }); +}); diff --git a/tests/e2e/specs/page-popup.test.ts b/tests/e2e/specs/page-popup.test.ts new file mode 100644 index 0000000..7a4805e --- /dev/null +++ b/tests/e2e/specs/page-popup.test.ts @@ -0,0 +1,12 @@ +import { canSwitchTheme } from '../helpers/theme.js'; + +describe('Webextension Popup', () => { + it('should open the popup successfully', async () => { + const extensionPath = await browser.getExtensionPath(); + const popupUrl = `${extensionPath}/popup/index.html`; + await browser.url(popupUrl); + + await expect(browser).toHaveTitle('Popup'); + await canSwitchTheme(); + }); +}); diff --git a/tests/e2e/specs/page-side-panel.test.ts b/tests/e2e/specs/page-side-panel.test.ts new file mode 100644 index 0000000..abbbb1d --- /dev/null +++ b/tests/e2e/specs/page-side-panel.test.ts @@ -0,0 +1,12 @@ +import { canSwitchTheme } from '../helpers/theme.js'; + +describe('Webextension Side Panel', () => { + it('should make side panel accessible', async () => { + const extensionPath = await browser.getExtensionPath(); + const sidePanelUrl = `${extensionPath}/side-panel/index.html`; + + await browser.url(sidePanelUrl); + await expect(browser).toHaveTitle('Side Panel'); + await canSwitchTheme(); + }); +}); diff --git a/tests/e2e/specs/smoke.test.ts b/tests/e2e/specs/smoke.test.ts new file mode 100644 index 0000000..ba2d9ac --- /dev/null +++ b/tests/e2e/specs/smoke.test.ts @@ -0,0 +1,7 @@ +describe('The example page can be loaded', () => { + it('should be able to go to example page', async () => { + await browser.url('https://www.example.com'); + + await expect(browser).toHaveTitle('Example Domain'); + }); +}); diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json new file mode 100644 index 0000000..2c67c9d --- /dev/null +++ b/tests/e2e/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@extension/tsconfig/module", + "compilerOptions": { + "lib": ["dom"], + "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"], + "resolveJsonModule": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["specs", "config", "helpers"] +} diff --git a/tests/e2e/utils/extension-path.ts b/tests/e2e/utils/extension-path.ts new file mode 100644 index 0000000..e863d0b --- /dev/null +++ b/tests/e2e/utils/extension-path.ts @@ -0,0 +1,49 @@ +/** + * Returns the Chrome extension path. + * @param browser + * @returns path to the Chrome extension + */ +export const getChromeExtensionPath = async (browser: WebdriverIO.Browser) => { + await browser.url('chrome://extensions/'); + /** + * https://webdriver.io/docs/extension-testing/web-extensions/#test-popup-modal-in-chrome + * ```ts + * const extensionItem = await $('extensions-item').getElement(); + * ``` + * The above code is not working. I guess it's because the shadow root is not accessible. + * So I used the following code to access the shadow root manually. + * + * @url https://github.com/webdriverio/webdriverio/issues/13521 + * @url https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/786 + */ + const extensionItem = await (async () => { + const extensionsManager = await $('extensions-manager').getElement(); + const itemList = await extensionsManager.shadow$('#container > #viewManager > extensions-item-list'); + return itemList.shadow$('extensions-item'); + })(); + + const extensionId = await extensionItem.getAttribute('id'); + + if (!extensionId) { + throw new Error('Extension ID not found'); + } + + return `chrome-extension://${extensionId}`; +}; + +/** + * Returns the Firefox extension path. + * @param browser + * @returns path to the Firefox extension + */ +export const getFirefoxExtensionPath = async (browser: WebdriverIO.Browser) => { + await browser.url('about:debugging#/runtime/this-firefox'); + const uuidElement = await browser.$('//dt[contains(text(), "Internal UUID")]/following-sibling::dd').getElement(); + const internalUUID = await uuidElement.getText(); + + if (!internalUUID) { + throw new Error('Internal UUID not found'); + } + + return `moz-extension://${internalUUID}`; +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2922c5f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "ESLint", + "extends": "./packages/tsconfig/base.json", + "compilerOptions": { + "noImplicitAny": false, + "noEmit": false, + "target": "ESNext" + }, + "include": ["eslint.config.ts"] +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..efa1bca --- /dev/null +++ b/turbo.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://turbo.build/schema.json", + "ui": "tui", + "globalEnv": ["CEB_*", "CLI_CEB_*"], + "globalDependencies": [".env"], + "concurrency": "12", + "tasks": { + "ready": { + "dependsOn": ["^ready"], + "outputs": ["../../dist/**", "dist/**"], + "cache": false + }, + "dev": { + "dependsOn": ["ready"], + "outputs": ["../../dist/**", "dist/**"], + "cache": false, + "persistent": true + }, + "build": { + "dependsOn": ["ready", "^build"], + "outputs": ["../../dist/**", "dist/**"], + "cache": false + }, + "e2e": { + "cache": false + }, + "type-check": { + "cache": false + }, + "lint": { + "cache": false + }, + "lint:fix": { + "cache": false + }, + "format": { + "dependsOn": ["^format"], + "cache": false + }, + "clean:node_modules": { + "dependsOn": ["^clean:node_modules"], + "cache": false + }, + "clean:turbo": { + "dependsOn": ["^clean:turbo"], + "cache": false + }, + "clean:bundle": { + "dependsOn": ["^clean:bundle"], + "cache": false + }, + "clean": { + "dependsOn": ["^clean"], + "cache": false + } + } +}