From a4d9a505261b552170d727db8d5d9cf13efd9668 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Thu, 22 Jan 2026 18:53:22 -0500 Subject: [PATCH 01/22] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Port=20to=20the?= =?UTF-8?q?=20gigachad.ts=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 12 + .github/.markdownlint.yaml | 3 + .github/workflows/copilot-setup-steps.yaml | 27 + .github/workflows/node.js.yaml | 115 + .gitignore | 219 + .markdownlint-cli2.yaml | 2 + .markdownlint.yaml | 6 + .prettierignore | 10 + .prettierrc.yaml | 4 + .vscode/extensions.json | 13 + .vscode/settings.json | 28 + LICENSE => LICENSE.txt | 1 + README.md | 38 +- esbuild.ts | 28 + eslint.config.ts | 99 + manifest.json | 26 + package.json | 62 + pnpm-lock.yaml | 4523 ++++++++++++++++++++ src/popup.css | 225 + src/popup.html | 68 + src/popup.ts | 5 + tsconfig.eslint.json | 5 + tsconfig.json | 64 + vitest.config.ts | 14 + 24 files changed, 5584 insertions(+), 13 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/.markdownlint.yaml create mode 100644 .github/workflows/copilot-setup-steps.yaml create mode 100644 .github/workflows/node.js.yaml create mode 100644 .gitignore create mode 100644 .markdownlint-cli2.yaml create mode 100644 .markdownlint.yaml create mode 100644 .prettierignore create mode 100644 .prettierrc.yaml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json rename LICENSE => LICENSE.txt (99%) create mode 100644 esbuild.ts create mode 100644 eslint.config.ts create mode 100644 manifest.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/popup.css create mode 100644 src/popup.html create mode 100644 src/popup.ts create mode 100644 tsconfig.eslint.json create mode 100644 tsconfig.json create mode 100644 vitest.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3693f0c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yaml,yml}] +indent_style = space diff --git a/.github/.markdownlint.yaml b/.github/.markdownlint.yaml new file mode 100644 index 0000000..b03557f --- /dev/null +++ b/.github/.markdownlint.yaml @@ -0,0 +1,3 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/DavidAnson/markdownlint/refs/heads/main/schema/markdownlint-config-schema.json +extends: ../.markdownlint.yaml +first-line-heading: false diff --git a/.github/workflows/copilot-setup-steps.yaml b/.github/workflows/copilot-setup-steps.yaml new file mode 100644 index 0000000..066a071 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yaml @@ -0,0 +1,27 @@ +name: Copilot Setup Steps + +on: + push: + paths: + - .github/workflows/copilot-setup-steps.yaml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yaml + +jobs: + copilot-setup-steps: + permissions: + contents: read + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v6 + with: + cache: pnpm + node-version: latest + - run: pnpm install diff --git a/.github/workflows/node.js.yaml b/.github/workflows/node.js.yaml new file mode 100644 index 0000000..3e478b5 --- /dev/null +++ b/.github/workflows/node.js.yaml @@ -0,0 +1,115 @@ +name: Node.js CI + +on: + merge_group: + branches: + - main + pull_request: + branches: + - main + push: + branches: + - main + workflow_dispatch: + +jobs: + test: + permissions: + contents: read + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v6 + with: + cache: pnpm + node-version: latest + + - run: pnpm install --frozen-lockfile --strict-peer-dependencies + - run: pnpm run build --noEmit + - run: pnpm run lint + - run: pnpm run test + - run: pnpm run test:coverage + + - if: github.actor != 'nektos/act' + uses: actions/upload-artifact@v6 + with: + name: coverage + path: coverage + compression-level: 9 + if-no-files-found: error + + fix: + permissions: + contents: write + + needs: test + if: failure() && github.event_name != 'merge_group' && github.actor != 'github-actions[bot]' && github.actor != 'nektos/act' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.head_ref || github.ref }} + - uses: pnpm/action-setup@v4 + with: + version: latest + - uses: actions/setup-node@v6 + with: + cache: pnpm + node-version: latest + + - run: | + pnpm install --fix-lockfile --no-frozen-lockfile + git add . + - id: commit-lockfile + uses: qoomon/actions--create-commit@v1 + with: + message: | + 📌 pnpm install --fix-lockfile + + [dependabot skip] + skip-empty: true + + - run: | + pnpm dlx ts-autofix --project tsconfig.json + git add . + - id: commit-ts-autofix + uses: qoomon/actions--create-commit@v1 + with: + message: | + 💚 ts-autofix --project tsconfig.json + + [dependabot skip] + skip-empty: true + + - run: | + pnpm run format + git add . + - id: commit-format + uses: qoomon/actions--create-commit@v1 + with: + message: | + 🎨 pnpm run format + + [dependabot skip] + skip-empty: true + + - run: | + pnpm run lint:fix + git add . + - id: commit-lint + uses: qoomon/actions--create-commit@v1 + with: + message: | + 🚨 pnpm run lint:fix + + [dependabot skip] + skip-empty: true + + - if: steps.commit-lockfile.outputs.commit || steps.commit-ts-autofix.outputs.commit || steps.commit-format.outputs.commit || steps.commit-lint.outputs.commit + run: git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfa3e3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,219 @@ +web-ext-artifacts/ + +# Created by https://www.toptal.com/developers/gitignore/api/node,linux,macos,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=node,linux,macos,windows + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/node,linux,macos,windows diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..230b283 --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,2 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/refs/heads/main/schema/markdownlint-cli2-config-schema.json +gitignore: true diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..52e3fea --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,6 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/DavidAnson/markdownlint/refs/heads/main/schema/markdownlint-config-schema.json +default: true +line-length: false +no-duplicate-heading: + siblings_only: true +no-inline-html: false diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..01546de --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +.pnpm-store/ +coverage/ +dist/ +docs/ +node_modules/ +out/ + +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..c0d38c9 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,4 @@ +arrowParens: avoid +semi: false +trailingComma: all +useTabs: true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b61df95 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + "recommendations": [ + "davidanson.vscode-markdownlint", + "dbaeumer.vscode-eslint", + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "github.vscode-github-actions", + "github.vscode-pull-request-github", + "ms-azuretools.vscode-containers", + "redhat.vscode-yaml", + "vitest.explorer" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..640ac6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,28 @@ +{ + "[css][dockercompose][github-actions-workflow][html][javascript][javascriptreact][json][jsonc][markdown][php][postcss][scss][sql][typescript][typescriptreact][xml][yaml]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.minimap.maxColumn": 80 + }, + "[css][dockercompose][github-actions-workflow][html][javascript][javascriptreact][json][jsonc][php][postcss][scss][svelte][typescript][typescriptreact][xml][yaml]": { + "editor.rulers": [80] + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "always", + "source.fixAll.markdownlint": "always", + "source.organizeImports": "always" + }, + "editor.formatOnSave": true, + "eslint.useFlatConfig": true, + "explorer.fileNesting.patterns": { + ".env": ".env.*", + ".env.*": ".env.${capture}.local", + "*.js": "${capture}.d.ts, ${capture}.d.ts.map, ${capture}.js.map, ${capture}.min.js, ${capture}.test.d.ts.map, ${capture}.test.js", + "*.mjs": "${capture}.d.mts, ${capture}.d.mts.map, ${capture}.mjs.map", + "*.mts": "${capture}.d.mts, ${capture}.d.mts.map, ${capture}.mjs, ${capture}.mjs.map", + "*.ts": "${capture}.test.ts, ${capture}.test-d.ts, ${capture}.test.ts.map, ${capture}.js", + "package.json": "bun.lockb, package-lock.json, pnpm-lock.yaml, yarn.lock", + "tsconfig.json": "tsconfig.*.json, tsconfig.tsbuildinfo", + "tsconfig.tsbuildinfo": "tsconfig.*.tsbuildinfo" + }, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/LICENSE b/LICENSE.txt similarity index 99% rename from LICENSE rename to LICENSE.txt index 261eeb9..d645695 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/README.md b/README.md index c878178..1afb1f8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # Houdini -Houdini is a chrome extension to show or hide comments from CodeRabbit and other bots in your GitHub pull request (PR). For CodeRabbit comments you can show or hide all comments or by severity levels. For other bots, you add the name of the bot and then you can show or hide all comments. -The visibility settings can be saved as default to apply on any PR page. When you change the settings for a specific PR tab, that setting is remembered till the tab is open. +Houdini is a chrome extension to show or hide comments from CodeRabbit and other bots in your GitHub pull request (PR). For CodeRabbit comments you can show or hide all comments or by severity levels. For other bots, you add the name of the bot and then you can show or hide all comments. -Houdini works by local manipulation of content in your browser page. It doesn't connect with any service or server in the backend. +The visibility settings can be saved as default to apply on any PR page. When you change the settings for a specific PR tab, that setting is remembered till the tab is open. -> Note: Houdini is experimental and not an officially supported Chrome Extension. Please read the license terms before using. +Houdini works by local manipulation of content in your browser page. It doesn't connect with any service or server in the backend. + +> [!NOTE] +> Houdini is experimental and not an officially supported Chrome Extension. Please read the license terms before using. ## Install @@ -15,19 +17,29 @@ git clone git@github.com:arvindsoni80/houdini.git In Chrome Browser, -* Open a tab and enter `chrome://extensions/` -* Turn on the `Developer mode` (toggle is on the top right corner) -* Click `Load unpacked` -* Browse and select the `houdini` directory inside the houdini repo you cloned. -* If you want you can `pin` the extension for easy access. +- Open a tab and enter `chrome://extensions/` +- Turn on the `Developer mode` (toggle is on the top right corner) +- Click `Load unpacked` +- Browse and select the `houdini` directory inside the houdini repo you cloned. +- If you want you can `pin` the extension for easy access. ![Install](docs/images/install.png "Install Houdini Chrome Extension") ## Usage -* On a GitHub PR page which has CodeRabbit and/or other bot comments, click on the Houdini extension. -* Toggle the various visibility filters and you should see the comments show/hide. For example, if you have CodeRabbit comments, the `All Hide` will hide all the comments. -* If you have other bots, add the name of the bot and toggle the visibility. For example, if you have `foo` bot comments, add `foo` in the `Other Bots` section and toggle its visibility to show/hide the comments. -* You save the settings as default and that will apply to all PR pages when they are open. +- On a GitHub PR page which has CodeRabbit and/or other bot comments, click on the Houdini extension. +- Toggle the various visibility filters and you should see the comments show/hide. For example, if you have CodeRabbit comments, the `All Hide` will hide all the comments. +- If you have other bots, add the name of the bot and toggle the visibility. For example, if you have `foo` bot comments, add `foo` in the `Other Bots` section and toggle its visibility to show/hide the comments. +- You save the settings as default and that will apply to all PR pages when they are open. ![Use](docs/images/overview.png "Houdini Overview") + +## License + +Copyright 2026 CodeRabbit + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +- + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/esbuild.ts b/esbuild.ts new file mode 100644 index 0000000..43e132c --- /dev/null +++ b/esbuild.ts @@ -0,0 +1,28 @@ +import * as esbuild from "esbuild" + +await esbuild.build({ + banner: { + js: `// Copyright 2026 CodeRabbit + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.`, + }, + bundle: true, + entryPoints: ["src/popup.ts"], + footer: { js: "// © 2026 CodeRabbit" }, + format: "esm", + loader: { ".ts": "ts" }, + outdir: "out", + sourcemap: true, + splitting: true, + treeShaking: true, +}) diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 0000000..ce95291 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,99 @@ +import { default as eslint } from "@eslint/js" +import { default as prettier } from "eslint-config-prettier" +import { defineConfig } from "eslint/config" +import { browser, node } from "globals" +import { default as tseslint } from "typescript-eslint" + +export default defineConfig( + { + languageOptions: { + globals: { ...browser, ...node }, + parserOptions: { project: "./tsconfig.eslint.json" }, + }, + }, + + eslint.configs.recommended, + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + prettier, + + { + rules: { + "@typescript-eslint/consistent-type-assertions": [ + "error", + { assertionStyle: "never" }, + ], + }, + ignores: ["**/*.test.ts"], + }, + + { + rules: { + "@typescript-eslint/class-methods-use-this": [ + "error", + { + ignoreClassesThatImplementAnInterface: true, + ignoreOverrideMethods: true, + }, + ], + "@typescript-eslint/consistent-type-exports": "error", + "@typescript-eslint/consistent-type-imports": [ + "error", + { fixStyle: "separate-type-imports" }, + ], + "@typescript-eslint/default-param-last": "error", + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { accessibility: "no-public" }, + ], + "@typescript-eslint/method-signature-style": "error", + "@typescript-eslint/no-import-type-side-effects": "error", + "@typescript-eslint/no-unnecessary-qualifier": "error", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-useless-empty-export": "error", + "@typescript-eslint/prefer-nullish-coalescing": [ + "error", + { ignorePrimitives: true }, + ], + "@typescript-eslint/prefer-readonly": "error", + "@typescript-eslint/prefer-regexp-exec": "error", + "@typescript-eslint/promise-function-async": [ + "error", + { checkArrowFunctions: false }, + ], + "@typescript-eslint/require-array-sort-compare": "error", + "@typescript-eslint/restrict-template-expressions": [ + "error", + { + allowBoolean: true, + allowNullish: true, + allowNumber: true, + allowRegExp: true, + }, + ], + "@typescript-eslint/return-await": "error", + "@typescript-eslint/sort-type-constituents": "error", + "@typescript-eslint/switch-exhaustiveness-check": "error", + "func-style": ["error", "declaration"], + }, + }, + + { extends: [tseslint.configs.disableTypeChecked], files: ["**/*.js"] }, + + { + ignores: [ + ".pnpm-store/", + "coverage/", + "dist/", + "docs/", + "node_modules/", + "out/", + + "package-lock.json", + "pnpm-lock.yaml", + ], + }, +) diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..4b4b392 --- /dev/null +++ b/manifest.json @@ -0,0 +1,26 @@ +{ + "manifest_version": 3, + "name": "Houdini", + "version": "0.0.0", + "description": "Show or hide bot comments on GitHub PRs", + "permissions": ["activeTab", "scripting", "storage"], + "host_permissions": ["https://github.com/*"], + "action": { + "default_popup": "src/popup.html", + "default_icon": { + "16": "houdini/icon16.png", + "48": "houdini/icon48.png", + "128": "houdini/icon128.png" + } + }, + "icons": { + "16": "houdini/icon16.png", + "48": "houdini/icon48.png", + "128": "houdini/icon128.png" + }, + "browser_specific_settings": { + "gecko": { + "id": "@coderabbitai-houdini" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2e5d6a0 --- /dev/null +++ b/package.json @@ -0,0 +1,62 @@ +{ + "name": "@coderabbitai/houdini", + "version": "0.0.0", + "description": "Show or hide bot comments on GitHub PRs", + "keywords": [ + "browser-extension", + "chrome-extension", + "coderabbit", + "github", + "pull-request", + "comments" + ], + "homepage": "https://github.com/coderabbitai/houdini", + "bugs": { + "url": "https://github.com/coderabbitai/houdini/issues" + }, + "license": "Apache-2.0", + "author": { + "name": "CodeRabbit", + "email": "support@coderabbit.ai", + "url": "https://github.com/coderabbitai" + }, + "contributors": [ + { + "name": "arvindsoni80", + "url": "https://github.com/arvindsoni80/houdini" + } + ], + "repository": "github:coderabbitai/houdini", + "scripts": { + "build": "node esbuild.ts", + "clean": "rm -rf coverage dist docs node_modules out tsconfig.tsbuildinfo", + "dev": "node esbuild.ts && web-ext run --devtools --no-reload", + "dev:watch": "node esbuild.ts && web-ext run --devtools", + "format": "prettier --write .", + "lint": "web-ext lint && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", + "lint:fix": "eslint --fix --quiet .; markdownlint-cli2 \"**/*.md\" --fix; prettier --list-different --write .", + "package": "web-ext build", + "start": "node esbuild.ts && web-ext run --no-reload", + "web-ext": "web-ext" + }, + "devDependencies": { + "@eslint/js": "^9.39.2", + "@types/webextension-polyfill": "^0.12.4", + "@typescript/native-preview": "7.0.0-dev.20260117.1", + "@vitest/coverage-v8": "^4.0.17", + "esbuild": "^0.27.2", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "globals": "^17.0.0", + "jiti": "^2.6.1", + "markdownlint-cli2": "^0.20.0", + "prettier": "^3.8.0", + "typedoc": "^0.28.16", + "typescript": "^5.9.3", + "typescript-eslint": "^8.53.0", + "vitest": "^4.0.17", + "web-ext": "^9.2.0", + "webextension-polyfill": "^0.12.0" + }, + "type": "module" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..d577deb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4523 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@eslint/js': + specifier: ^9.39.2 + version: 9.39.2 + '@types/webextension-polyfill': + specifier: ^0.12.4 + version: 0.12.4 + '@typescript/native-preview': + specifier: 7.0.0-dev.20260117.1 + version: 7.0.0-dev.20260117.1 + '@vitest/coverage-v8': + specifier: ^4.0.17 + version: 4.0.17(vitest@4.0.17(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2)) + esbuild: + specifier: ^0.27.2 + version: 0.27.2 + eslint: + specifier: ^9.39.2 + version: 9.39.2(jiti@2.6.1) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.2(jiti@2.6.1)) + globals: + specifier: ^17.0.0 + version: 17.0.0 + jiti: + specifier: ^2.6.1 + version: 2.6.1 + markdownlint-cli2: + specifier: ^0.20.0 + version: 0.20.0 + prettier: + specifier: ^3.8.0 + version: 3.8.0 + typedoc: + specifier: ^0.28.16 + version: 0.28.16(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.53.0 + version: 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + vitest: + specifier: ^4.0.17 + version: 4.0.17(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2) + web-ext: + specifier: ^9.2.0 + version: 9.2.0(jiti@2.6.1) + webextension-polyfill: + specifier: ^0.12.0 + version: 0.12.0 + +packages: + + '@babel/code-frame@7.28.6': + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.6': + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@devicefarmer/adbkit-logcat@2.1.3': + resolution: {integrity: sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==} + engines: {node: '>= 4'} + + '@devicefarmer/adbkit-monkey@1.2.1': + resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==} + engines: {node: '>= 0.10.4'} + + '@devicefarmer/adbkit@3.3.8': + resolution: {integrity: sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==} + engines: {node: '>= 0.10.4'} + hasBin: true + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + 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.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.0': + resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fluent/syntax@0.19.0': + resolution: {integrity: sha512-5D2qVpZrgpjtqU4eNOcWGp1gnUCgjfM+vKGE2y03kKN6z5EBhtx0qdRFbg8QuNNj8wXNoX93KJoYb+NqoxswmQ==} + engines: {node: '>=14.0.0', npm: '>=7.0.0'} + + '@fregante/relaxed-json@2.0.0': + resolution: {integrity: sha512-PyUXQWB42s4jBli435TDiYuVsadwRHnMc27YaLouINktvTWsL3FcKrRMGawTayFk46X+n5bE23RjUTWQwrukWw==} + engines: {node: '>= 0.10.0'} + + '@gerrit0/mini-shiki@3.21.0': + resolution: {integrity: sha512-9PrsT5DjZA+w3lur/aOIx3FlDeHdyCEFlv9U+fmsVyjPZh61G5SYURQ/1ebe2U63KbDmI2V8IhIUegWb8hjOyg==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mdn/browser-compat-data@7.1.22': + resolution: {integrity: sha512-pvfjbTDEYTDNKl9u3KbRzeNmU7PrcsFiGzIUbcB9JPq1LziTMs6YpE2x3Gf+2gHOPQzdO/KQ8hAr1Kkkzpklrg==} + + '@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'} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.2': + resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} + engines: {node: '>=12'} + + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} + cpu: [x64] + os: [win32] + + '@shikijs/engine-oniguruma@3.21.0': + resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==} + + '@shikijs/langs@3.21.0': + resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==} + + '@shikijs/themes@3.21.0': + resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==} + + '@shikijs/types@3.21.0': + resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/katex@0.16.8': + resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} + + '@types/minimatch@3.0.5': + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@25.0.9': + resolution: {integrity: sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/webextension-polyfill@0.12.4': + resolution: {integrity: sha512-wK8YdSI0pDiaehSLDIvtvonYmLwUUivg4Z6JCJO8rkyssMAG82cFJgwPK/V7NO61mJBLg/tXeoXQL8AFzpXZmQ==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@8.53.0': + resolution: {integrity: sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.53.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.53.0': + resolution: {integrity: sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==} + 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.53.0': + resolution: {integrity: sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.53.0': + resolution: {integrity: sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.53.0': + resolution: {integrity: sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.53.0': + resolution: {integrity: sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==} + 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.53.0': + resolution: {integrity: sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.0': + resolution: {integrity: sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.0': + resolution: {integrity: sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==} + 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.53.0': + resolution: {integrity: sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-sf8/7keXq1auS7XI0vyAs3t/vgss4/EwACZmzSYtO75dug8TRmuSQDPHxx4R16VURijO/GQ3Bz6rA8VivREzsA==} + cpu: [arm64] + os: [darwin] + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-3DhQdRUbDq7YduIaRYJtH5MXqCzOry+GT4DjkLPJ08vOGOFypZjgd6G+moEQHgitnefisC1PkNcL1baF4B7+og==} + cpu: [x64] + os: [darwin] + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-R9h8cX5C0yBtE8xnpAxZBSHWPiVW3d5o3mup0ei1n/8w98lwtMFLWURnPD8N4kRgql9L40IbRXW6jqWdjDUe4Q==} + cpu: [arm64] + os: [linux] + + '@typescript/native-preview-linux-arm@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-RKSKINs23agj0MJJ2Tzkhe/MuwhcPqRNRRfLmpVRjrNWI43Ta6a5+5Lah+7GYjt00wHUOhay94ZT3iHJkeHATg==} + cpu: [arm] + os: [linux] + + '@typescript/native-preview-linux-x64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-KI5UprUMIVT4wehBcbGZQm0I3z1uls8fsOmCUzdEpmaLpVu8NbTahgJPVtMyS/nDUiN3At4Uj8ciL36kgbqRmQ==} + cpu: [x64] + os: [linux] + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-dUJTLMD24TQ4XiyYAxxqKs6hPrq8wS9BiFt+ucjCuWXBGQLbnfiT4bgYz2a4yfbtyxVkG7mxV8udo+gRXtaWRQ==} + cpu: [arm64] + os: [win32] + + '@typescript/native-preview-win32-x64@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-SdKCAtNZee2TMdZ/bGjfKWX43qIAZhBwLlkPU1G0uxKqmDG1mVRbg/krxm5MnKOjFL00X/gozth6d3LmimonnA==} + cpu: [x64] + os: [win32] + + '@typescript/native-preview@7.0.0-dev.20260117.1': + resolution: {integrity: sha512-5peFvGyf+TOlU6KYMLzfPiPJdxYwG0O/oQN4Z+EzDOzrN8tbY/H58+QOQww4Lo8m4b7+4o/0r+zopD/sYR6uMw==} + hasBin: true + + '@vitest/coverage-v8@4.0.17': + resolution: {integrity: sha512-/6zU2FLGg0jsd+ePZcwHRy3+WpNTBBhDY56P4JTRqUN/Dp6CvOEa9HrikcQ4KfV2b2kAHUFB4dl1SuocWXSFEw==} + peerDependencies: + '@vitest/browser': 4.0.17 + vitest: 4.0.17 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.0.17': + resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==} + + '@vitest/mocker@4.0.17': + resolution: {integrity: sha512-+ZtQhLA3lDh1tI2wxe3yMsGzbp7uuJSWBM1iTIKCbppWTSBN09PUC+L+fyNlQApQoR+Ps8twt2pbSSXg2fQVEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.17': + resolution: {integrity: sha512-Ah3VAYmjcEdHg6+MwFE17qyLqBHZ+ni2ScKCiW2XrlSBV4H3Z7vYfPfz7CWQ33gyu76oc0Ai36+kgLU3rfF4nw==} + + '@vitest/runner@4.0.17': + resolution: {integrity: sha512-JmuQyf8aMWoo/LmNFppdpkfRVHJcsgzkbCA+/Bk7VfNH7RE6Ut2qxegeyx2j3ojtJtKIbIGy3h+KxGfYfk28YQ==} + + '@vitest/snapshot@4.0.17': + resolution: {integrity: sha512-npPelD7oyL+YQM2gbIYvlavlMVWUfNNGZPcu0aEUQXt7FXTuqhmgiYupPnAanhKvyP6Srs2pIbWo30K0RbDtRQ==} + + '@vitest/spy@4.0.17': + resolution: {integrity: sha512-I1bQo8QaP6tZlTomQNWKJE6ym4SHf3oLS7ceNjozxxgzavRAgZDc06T7kD8gb9bXKEgcLNt00Z+kZO6KaJ62Ew==} + + '@vitest/utils@4.0.17': + resolution: {integrity: sha512-RG6iy+IzQpa9SB8HAFHJ9Y+pTzI+h8553MrciN9eC6TFBErqrQaTas4vG+MVj8S4uKk8uTT2p0vgZPnTdxd96w==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + addons-linter@9.2.0: + resolution: {integrity: sha512-0rPW6qjbLjBRBT02NZoY9wSc4KfwVV9tWJ0YHPOTT90yULJdYfv6ZzrVSvRyjm+jtqYyg06K0kSVkK3Gmp3yfA==} + engines: {node: '>=18.0.0'} + hasBin: true + + addons-moz-compare@1.3.0: + resolution: {integrity: sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==} + + addons-scanner-utils@9.14.0: + resolution: {integrity: sha512-CXG/r041S/eElF/XNcEtfNtFQERxzhVWFCXcdAB41kqDZ1GmFKA8gbPftstZxNKwLl7Y8yBW2jdmGv4Wd/LTWA==} + peerDependencies: + body-parser: 1.20.3 + express: 4.21.2 + node-fetch: 2.6.11 + safe-compare: 1.1.4 + peerDependenciesMeta: + body-parser: + optional: true + express: + optional: true + node-fetch: + optional: true + safe-compare: + optional: true + + adm-zip@0.5.16: + resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} + engines: {node: '>=12.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + 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-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + 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@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-differ@4.0.0: + resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + array-union@3.0.1: + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + engines: {node: '>=12'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@0.3.10: + resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + atomically@2.1.0: + resolution: {integrity: sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + 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'} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + 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'} + + chrome-launcher@1.2.0: + resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==} + engines: {node: '>=12.13.0'} + hasBin: true + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + 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'} + + 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==} + + columnify@1.6.0: + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} + + commander@2.9.0: + resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==} + engines: {node: '>= 0.6.x'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@7.1.0: + resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} + engines: {node: '>=18'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@6.0.1: + resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + engines: {node: '>=18'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + 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==} + + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + 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'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-no-unsanitized@4.1.4: + resolution: {integrity: sha512-cjAoZoq3J+5KJuycYYOWrc0/OpZ7pl2Z3ypfFq4GtaAgheg+L7YGxUo2YS3avIvo/dYU5/zR2hXu3v81M9NxhQ==} + peerDependencies: + eslint: ^8 || ^9 + + 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.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.0: + resolution: {integrity: sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.0: + resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.0.0: + resolution: {integrity: sha512-+gMeWRrIh/NsG+3NaLeWHuyeyk70p2tbvZIWBYcqQ4/7Xvars6GYTZNhF1sIeLcc6Wb11He5ffz3hsHyXFrw5A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-patch@3.1.1: + resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} + + 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==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + 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'} + + firefox-profile@4.7.0: + resolution: {integrity: sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==} + engines: {node: '>=18'} + hasBin: true + + first-chunk-stream@3.0.0: + resolution: {integrity: sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==} + engines: {node: '>=8'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fx-runner@1.4.0: + resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==} + 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'} + + 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==} + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.0.0: + resolution: {integrity: sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==} + engines: {node: '>=18'} + + globby@15.0.0: + resolution: {integrity: sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==} + engines: {node: '>=20'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graceful-readlink@1.0.1: + resolution: {integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==} + + growly@1.3.0: + resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + 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'} + + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + is-absolute@0.1.7: + resolution: {integrity: sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==} + engines: {node: '>=0.10.0'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + + is-npm@6.1.0: + resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-relative@0.1.3: + resolution: {integrity: sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==} + engines: {node: '>=0.10.0'} + + is-utf8@0.2.1: + resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@1.1.2: + resolution: {integrity: sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-merge-patch@1.0.2: + resolution: {integrity: sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==} + + 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==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + katex@0.16.27: + resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + ky@1.14.2: + resolution: {integrity: sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==} + engines: {node: '>=18'} + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + + 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==} + + lighthouse-logger@2.0.2: + resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + markdownlint-cli2-formatter-default@0.0.6: + resolution: {integrity: sha512-VVDGKsq9sgzu378swJ0fcHfSicUnMxnL8gnLm/Q4J/xsNJ4e5bA6lvAz7PCzIl0/No0lHyaWdqVD2jotxOSFMQ==} + peerDependencies: + markdownlint-cli2: '>=0.0.4' + + markdownlint-cli2@0.20.0: + resolution: {integrity: sha512-esPk+8Qvx/f0bzI7YelUeZp+jCtFOk3KjZ7s9iBQZ6HlymSXoTtWGiIRZP05/9Oy2ehIoIjenVwndxGtxOIJYQ==} + engines: {node: '>=20'} + hasBin: true + + markdownlint@0.40.0: + resolution: {integrity: sha512-UKybllYNheWac61Ia7T6fzuQNDZimFIpCg2w6hHjgV1Qu0w1TV0LlSgryUGzM0bkKQCBhy2FDhEELB73Kb0kAg==} + engines: {node: '>=20'} + + marky@1.3.0: + resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@4.0.0: + resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + 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==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multimatch@6.0.0: + resolution: {integrity: sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-forge@1.3.3: + resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} + engines: {node: '>= 6.13.0'} + + node-notifier@10.0.1: + resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-shim@0.1.3: + resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} + engines: {node: '>= 0.4.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + + 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-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + 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-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + 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.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.1.0: + resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} + hasBin: true + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.8.0: + resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + promise-toolbox@0.21.0: + resolution: {integrity: sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==} + engines: {node: '>=6'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.3.0: + resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==} + engines: {node: '>=12.20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + registry-auth-token@5.1.1: + resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + 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'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.4.4: + resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} + engines: {node: '>=11.0.0'} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + 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'} + + shell-quote@1.7.3: + resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} + + shellwords@0.1.1: + resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + + 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'} + + spawn-sync@1.0.15: + resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@1.0.1: + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.1.0: + resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} + engines: {node: '>=20'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + 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-buf@2.0.0: + resolution: {integrity: sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==} + engines: {node: '>=8'} + + strip-bom-stream@4.0.0: + resolution: {integrity: sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==} + engines: {node: '>=8'} + + strip-bom@5.0.0: + resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typedoc@0.28.16: + resolution: {integrity: sha512-x4xW77QC3i5DUFMBp0qjukOTnr/sSg+oEs86nB3LjDslvAmwe/PUGDWbe3GrIqt59oTqoXK5GRK9tAa0sYMiog==} + engines: {node: '>= 18', pnpm: '>= 10'} + hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x + + typescript-eslint@8.53.0: + resolution: {integrity: sha512-xHURCQNxZ1dsWn0sdOaOfCSQG0HKeqSj9OexIxrz6ypU6wHYOdX2I3D2b8s8wFSsSOYJb+6q283cLiLlkEsBYw==} + 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.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.18.2: + resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} + engines: {node: '>=20.18.1'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + upath@2.0.1: + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + engines: {node: '>=4'} + + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} + engines: {node: '>=18'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.17: + resolution: {integrity: sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.17 + '@vitest/browser-preview': 4.0.17 + '@vitest/browser-webdriverio': 4.0.17 + '@vitest/ui': 4.0.17 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: 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-ext@9.2.0: + resolution: {integrity: sha512-25xjkR/MaP7vbXyAWLYpMO6AnVvsxDXtxhSz9sev+OiM3MkDktnwFZNMq8l/J3gtmc9XAC9lJUgCppBwI/T0tw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + webextension-polyfill@0.12.0: + resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + + when@3.7.7: + resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==} + + which@1.2.4: + resolution: {integrity: sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + winreg@0.0.12: + resolution: {integrity: sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + 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==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zip-dir@2.0.0: + resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==} + +snapshots: + + '@babel/code-frame@7.28.6': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.6': + dependencies: + '@babel/types': 7.28.6 + + '@babel/runtime@7.28.4': {} + + '@babel/types@7.28.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@1.0.2': {} + + '@devicefarmer/adbkit-logcat@2.1.3': {} + + '@devicefarmer/adbkit-monkey@1.2.1': {} + + '@devicefarmer/adbkit@3.3.8': + dependencies: + '@devicefarmer/adbkit-logcat': 2.1.3 + '@devicefarmer/adbkit-monkey': 1.2.1 + bluebird: 3.7.2 + commander: 9.5.0 + debug: 4.3.7 + node-forge: 1.3.3 + split: 1.0.1 + transitivePeerDependencies: + - supports-color + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.0(jiti@2.6.1))': + dependencies: + eslint: 9.39.0(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': + dependencies: + eslint: 9.39.2(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.0': {} + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@fluent/syntax@0.19.0': {} + + '@fregante/relaxed-json@2.0.0': {} + + '@gerrit0/mini-shiki@3.21.0': + dependencies: + '@shikijs/engine-oniguruma': 3.21.0 + '@shikijs/langs': 3.21.0 + '@shikijs/themes': 3.21.0 + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mdn/browser-compat-data@7.1.22': {} + + '@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.20.1 + + '@pinojs/redact@0.4.0': {} + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@rollup/rollup-android-arm-eabi@4.55.1': + optional: true + + '@rollup/rollup-android-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.55.1': + optional: true + + '@rollup/rollup-darwin-x64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.55.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.55.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.55.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.55.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.55.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@shikijs/engine-oniguruma@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/themes@3.21.0': + dependencies: + '@shikijs/types': 3.21.0 + + '@shikijs/types@3.21.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/katex@0.16.8': {} + + '@types/minimatch@3.0.5': {} + + '@types/ms@2.1.0': {} + + '@types/node@25.0.9': + dependencies: + undici-types: 7.16.0 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/webextension-polyfill@0.12.4': {} + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 25.0.9 + + '@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/type-utils': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + eslint: 9.39.2(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + + '@typescript-eslint/tsconfig-utils@8.53.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.6.1) + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.0': {} + + '@typescript-eslint/typescript-estree@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + eslint-visitor-keys: 4.2.1 + + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-linux-arm@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-linux-x64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview-win32-x64@7.0.0-dev.20260117.1': + optional: true + + '@typescript/native-preview@7.0.0-dev.20260117.1': + optionalDependencies: + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260117.1 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260117.1 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260117.1 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260117.1 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260117.1 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260117.1 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260117.1 + + '@vitest/coverage-v8@4.0.17(vitest@4.0.17(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.17 + ast-v8-to-istanbul: 0.3.10 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.17(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2) + + '@vitest/expect@4.0.17': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.17 + '@vitest/utils': 4.0.17 + chai: 6.2.2 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.17(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.17 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2) + + '@vitest/pretty-format@4.0.17': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.17': + dependencies: + '@vitest/utils': 4.0.17 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.17': + dependencies: + '@vitest/pretty-format': 4.0.17 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.17': {} + + '@vitest/utils@4.0.17': + dependencies: + '@vitest/pretty-format': 4.0.17 + tinyrainbow: 3.0.3 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + addons-linter@9.2.0(jiti@2.6.1): + dependencies: + '@fluent/syntax': 0.19.0 + '@fregante/relaxed-json': 2.0.0 + '@mdn/browser-compat-data': 7.1.22 + addons-moz-compare: 1.3.0 + addons-scanner-utils: 9.14.0 + ajv: 8.17.1 + chalk: 4.1.2 + cheerio: 1.1.2 + columnify: 1.6.0 + common-tags: 1.8.2 + deepmerge: 4.3.1 + eslint: 9.39.0(jiti@2.6.1) + eslint-plugin-no-unsanitized: 4.1.4(eslint@9.39.0(jiti@2.6.1)) + eslint-visitor-keys: 5.0.0 + espree: 11.0.0 + esprima: 4.0.1 + fast-json-patch: 3.1.1 + image-size: 2.0.2 + json-merge-patch: 1.0.2 + pino: 10.1.0 + semver: 7.7.3 + source-map-support: 0.5.21 + upath: 2.0.1 + yargs: 17.7.2 + yauzl: 2.10.0 + transitivePeerDependencies: + - body-parser + - express + - jiti + - node-fetch + - safe-compare + - supports-color + + addons-moz-compare@1.3.0: {} + + addons-scanner-utils@9.14.0: + dependencies: + '@types/yauzl': 2.10.3 + common-tags: 1.8.2 + first-chunk-stream: 3.0.0 + strip-bom-stream: 4.0.0 + upath: 2.0.1 + yauzl: 2.10.0 + + adm-zip@0.5.16: {} + + agent-base@7.1.4: {} + + 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-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + argparse@2.0.1: {} + + array-differ@4.0.0: {} + + array-union@3.0.1: {} + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@0.3.10: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + + async@3.2.6: {} + + atomic-sleep@1.0.0: {} + + atomically@2.1.0: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + + balanced-match@1.0.2: {} + + bluebird@3.7.2: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-crc32@0.2.13: {} + + buffer-from@1.1.2: {} + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + callsites@3.1.0: {} + + camelcase@8.0.0: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + 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.18.2 + whatwg-mimetype: 4.0.0 + + chrome-launcher@1.2.0: + dependencies: + '@types/node': 25.0.9 + escape-string-regexp: 4.0.0 + is-wsl: 2.2.0 + lighthouse-logger: 2.0.2 + transitivePeerDependencies: + - supports-color + + cli-boxes@3.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: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + columnify@1.6.0: + dependencies: + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + commander@2.9.0: + dependencies: + graceful-readlink: 1.0.1 + + commander@8.3.0: {} + + commander@9.5.0: {} + + common-tags@1.8.2: {} + + concat-map@0.0.1: {} + + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@7.1.0: + dependencies: + atomically: 2.1.0 + dot-prop: 9.0.0 + graceful-fs: 4.2.11 + xdg-basedir: 5.1.0 + + core-util-is@1.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + 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-what@6.2.2: {} + + debounce@1.2.1: {} + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@6.0.1: {} + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.4.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-lazy-prop@3.0.0: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.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 + + dot-prop@9.0.0: + dependencies: + type-fest: 4.41.0 + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + entities@4.5.0: {} + + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + + es6-error@4.1.1: {} + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escalade@3.2.0: {} + + escape-goat@4.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): + dependencies: + eslint: 9.39.2(jiti@2.6.1) + + eslint-plugin-no-unsanitized@4.1.4(eslint@9.39.0(jiti@2.6.1)): + dependencies: + eslint: 9.39.0(jiti@2.6.1) + + 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.1: {} + + eslint-visitor-keys@5.0.0: {} + + eslint@9.39.0(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.0(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.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.6.1 + transitivePeerDependencies: + - supports-color + + eslint@9.39.2(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.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.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + espree@11.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 5.0.0 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + expect-type@1.3.0: {} + + fast-deep-equal@3.1.3: {} + + 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-patch@3.1.1: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + 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 + + firefox-profile@4.7.0: + dependencies: + adm-zip: 0.5.16 + fs-extra: 11.3.3 + ini: 4.1.3 + minimist: 1.2.8 + xml2js: 0.6.2 + + first-chunk-stream@3.0.0: {} + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + fs-extra@11.3.3: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + fx-runner@1.4.0: + dependencies: + commander: 2.9.0 + shell-quote: 1.7.3 + spawn-sync: 1.0.15 + when: 3.7.7 + which: 1.2.4 + winreg: 0.0.12 + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + + 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: {} + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + globals@14.0.0: {} + + globals@17.0.0: {} + + globby@15.0.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + graceful-readlink@1.0.1: {} + + growly@1.3.0: {} + + has-flag@4.0.0: {} + + html-escaper@2.0.2: {} + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + image-size@2.0.2: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + index-to-position@1.2.0: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + ini@4.1.3: {} + + is-absolute@0.1.7: + dependencies: + is-relative: 0.1.3 + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-in-ci@1.0.0: {} + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + + is-npm@6.1.0: {} + + is-number@7.0.0: {} + + is-path-inside@4.0.0: {} + + is-relative@0.1.3: {} + + is-utf8@0.2.1: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isarray@1.0.0: {} + + isexe@1.1.2: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jiti@2.6.1: {} + + jose@5.9.6: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-merge-patch@1.0.2: + dependencies: + fast-deep-equal: 3.1.3 + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonc-parser@3.3.1: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + katex@0.16.27: + dependencies: + commander: 8.3.0 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + ky@1.14.2: {} + + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lighthouse-logger@2.0.2: + dependencies: + debug: 4.4.3 + marky: 1.3.0 + transitivePeerDependencies: + - supports-color + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lunr@2.3.9: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + + make-error@1.3.6: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdownlint-cli2-formatter-default@0.0.6(markdownlint-cli2@0.20.0): + dependencies: + markdownlint-cli2: 0.20.0 + + markdownlint-cli2@0.20.0: + dependencies: + globby: 15.0.0 + js-yaml: 4.1.1 + jsonc-parser: 3.3.1 + markdown-it: 14.1.0 + markdownlint: 0.40.0 + markdownlint-cli2-formatter-default: 0.0.6(markdownlint-cli2@0.20.0) + micromatch: 4.0.8 + transitivePeerDependencies: + - supports-color + + markdownlint@0.40.0: + dependencies: + micromark: 4.0.2 + micromark-core-commonmark: 2.0.3 + micromark-extension-directive: 4.0.0 + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-math: 3.1.0 + micromark-util-types: 2.0.2 + string-width: 8.1.0 + transitivePeerDependencies: + - supports-color + + marky@1.3.0: {} + + mdurl@2.0.0: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@4.0.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.8 + devlop: 1.1.0 + katex: 0.16.27 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + multimatch@6.0.0: + dependencies: + '@types/minimatch': 3.0.5 + array-differ: 4.0.0 + array-union: 3.0.1 + minimatch: 3.1.2 + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-forge@1.3.3: {} + + node-notifier@10.0.1: + dependencies: + growly: 1.3.0 + is-wsl: 2.2.0 + semver: 7.7.3 + shellwords: 0.1.1 + uuid: 8.3.2 + which: 2.0.2 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + obug@2.1.1: {} + + on-exit-leak-free@2.1.2: {} + + open@11.0.0: + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.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-shim@0.1.3: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json@10.0.1: + dependencies: + ky: 1.14.2 + registry-auth-token: 5.1.1 + registry-url: 6.0.1 + semver: 7.7.3 + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.28.6 + index-to-position: 1.2.0 + type-fest: 4.41.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-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-type@6.0.0: {} + + pathe@2.0.3: {} + + pend@1.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@10.1.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + powershell-utils@0.1.0: {} + + prelude-ls@1.2.1: {} + + prettier@3.8.0: {} + + process-nextick-args@2.0.1: {} + + process-warning@5.0.0: {} + + promise-toolbox@0.21.0: + dependencies: + make-error: 1.3.6 + + proto-list@1.2.4: {} + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + pupa@3.3.0: + dependencies: + escape-goat: 4.0.0 + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + 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 + + real-require@0.2.0: {} + + registry-auth-token@5.1.1: + dependencies: + '@pnpm/npm-conf': 3.0.2 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rollup@4.55.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 + fsevents: 2.3.3 + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + sax@1.4.4: {} + + semver@7.7.3: {} + + setimmediate@1.0.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.7.3: {} + + shellwords@0.1.1: {} + + siginfo@2.0.0: {} + + slash@5.1.0: {} + + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.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: {} + + spawn-sync@1.0.15: + dependencies: + concat-stream: 1.6.2 + os-shim: 0.1.3 + + split2@4.2.0: {} + + split@1.0.1: + dependencies: + through: 2.3.8 + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + 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@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string-width@8.1.0: + dependencies: + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom-buf@2.0.0: + dependencies: + is-utf8: 0.2.1 + + strip-bom-stream@4.0.0: + dependencies: + first-chunk-stream: 3.0.0 + strip-bom-buf: 2.0.0 + + strip-bom@5.0.0: {} + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strip-json-comments@5.0.3: {} + + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + through@2.3.8: {} + + tinybench@2.9.0: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + + tmp@0.2.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@4.41.0: {} + + typedarray@0.0.6: {} + + typedoc@0.28.16(typescript@5.9.3): + dependencies: + '@gerrit0/mini-shiki': 3.21.0 + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.9.3 + yaml: 2.8.2 + + typescript-eslint@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + uc.micro@2.1.0: {} + + undici-types@7.16.0: {} + + undici@7.18.2: {} + + unicorn-magic@0.3.0: {} + + universalify@2.0.1: {} + + upath@2.0.1: {} + + update-notifier@7.3.1: + dependencies: + boxen: 8.0.1 + chalk: 5.6.2 + configstore: 7.1.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 + is-npm: 6.1.0 + latest-version: 9.0.0 + pupa: 3.3.0 + semver: 7.7.3 + xdg-basedir: 5.1.0 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@8.3.2: {} + + vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.55.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.0.9 + fsevents: 2.3.3 + jiti: 2.6.1 + yaml: 2.8.2 + + vitest@4.0.17(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.17 + '@vitest/mocker': 4.0.17(vite@7.3.1(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.17 + '@vitest/runner': 4.0.17 + '@vitest/snapshot': 4.0.17 + '@vitest/spy': 4.0.17 + '@vitest/utils': 4.0.17 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@25.0.9)(jiti@2.6.1)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 25.0.9 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + 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 + + web-ext@9.2.0(jiti@2.6.1): + dependencies: + '@babel/runtime': 7.28.4 + '@devicefarmer/adbkit': 3.3.8 + addons-linter: 9.2.0(jiti@2.6.1) + camelcase: 8.0.0 + chrome-launcher: 1.2.0 + debounce: 1.2.1 + decamelize: 6.0.1 + es6-error: 4.1.1 + firefox-profile: 4.7.0 + fx-runner: 1.4.0 + https-proxy-agent: 7.0.6 + jose: 5.9.6 + jszip: 3.10.1 + multimatch: 6.0.0 + node-notifier: 10.0.1 + open: 11.0.0 + parse-json: 8.3.0 + pino: 10.1.0 + promise-toolbox: 0.21.0 + source-map-support: 0.5.21 + strip-bom: 5.0.0 + strip-json-comments: 5.0.3 + tmp: 0.2.5 + update-notifier: 7.3.1 + watchpack: 2.4.4 + yargs: 17.7.2 + zip-dir: 2.0.0 + transitivePeerDependencies: + - body-parser + - express + - jiti + - node-fetch + - safe-compare + - supports-color + + webextension-polyfill@0.12.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + when-exit@2.1.5: {} + + when@3.7.7: {} + + which@1.2.4: + dependencies: + is-absolute: 0.1.7 + isexe: 1.1.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + winreg@0.0.12: {} + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + wsl-utils@0.3.1: + dependencies: + is-wsl: 3.1.0 + powershell-utils: 0.1.0 + + xdg-basedir@5.1.0: {} + + xml2js@0.6.2: + dependencies: + sax: 1.4.4 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + y18n@5.0.8: {} + + yaml@2.8.2: {} + + yargs-parser@21.1.1: {} + + 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 + + yocto-queue@0.1.0: {} + + zip-dir@2.0.0: + dependencies: + async: 3.2.6 + jszip: 3.10.1 diff --git a/src/popup.css b/src/popup.css new file mode 100644 index 0000000..d09d066 --- /dev/null +++ b/src/popup.css @@ -0,0 +1,225 @@ +body { + width: 320px; + padding: 15px; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; +} +.header { + margin-bottom: 16px; +} +h1 { + margin: 0 0 2px 0; + font-size: 18px; + font-weight: 600; + color: #24292f; +} +.subtext { + margin: 0; + font-size: 12px; + font-weight: 400; + color: #24292f; +} +.subtext .show-label { + background-color: #2da44e; + color: white; + padding: 2px 6px; + border-radius: 3px; + font-weight: 500; +} +.subtext .hide-label { + background-color: #ccc; + color: #24292f; + padding: 2px 6px; + border-radius: 3px; + font-weight: 500; +} +.subtext .separator { + margin: 0 4px; + color: #57606a; +} +.section-title { + font-size: 14px; + font-weight: 600; + color: #24292f; + margin-bottom: 8px; +} +.filter-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + background-color: #f6f8fa; + border-radius: 6px; + margin-bottom: 8px; +} +.severity-label { + font-size: 13px; + font-weight: 500; + display: flex; + align-items: center; + gap: 6px; +} +.count { + color: #57606a; + font-size: 11px; +} + +/* Toggle Switch Styles */ +.toggle-switch { + position: relative; + display: inline-block; + width: 44px; + height: 24px; +} +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: 0.3s; + border-radius: 24px; +} +.slider:before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 3px; + bottom: 3px; + background-color: white; + transition: 0.3s; + border-radius: 50%; +} +input:checked + .slider { + background-color: #2da44e; +} +input:checked + .slider:before { + transform: translateX(20px); +} +.slider:hover { + opacity: 0.9; +} + +.divider { + margin: 15px 0; + border-top: 1px solid #d0d7de; +} +.save-btn { + width: 100%; + padding: 8px; + font-size: 13px; + border: 1px solid #1f6feb; + border-radius: 6px; + background-color: #1f6feb; + color: white; + cursor: pointer; + font-weight: 500; + margin-top: 12px; +} +.save-btn:hover { + background-color: #1a5ecc; + border-color: #1a5ecc; +} + +/* Custom bot styles */ +.bot-section { + margin-top: 12px; +} +.bot-input-group { + display: flex; + gap: 6px; + margin-bottom: 10px; +} +#botNameInput { + flex: 1; + padding: 6px 8px; + font-size: 12px; + border: 1px solid #d0d7de; + border-radius: 4px; + font-family: inherit; +} +#botNameInput:focus { + outline: none; + border-color: #1f6feb; +} +#addBotBtn { + padding: 6px 12px; + font-size: 12px; + border: 1px solid #1f6feb; + border-radius: 4px; + background-color: #1f6feb; + color: white; + cursor: pointer; + font-weight: 500; +} +#addBotBtn:hover { + background-color: #1a5ecc; +} +#customBotList { + max-height: 200px; + overflow-y: auto; +} +.custom-bot-item { + display: flex; + align-items: center; + padding: 6px 8px; + background-color: #f6f8fa; + border-radius: 4px; + margin-bottom: 6px; + gap: 8px; +} +.bot-name { + font-size: 12px; + font-weight: 500; + color: #24292f; + flex: 1; +} +.remove-btn { + padding: 2px 8px; + font-size: 14px; + border: 1px solid #d0d7de; + border-radius: 4px; + background-color: white; + color: #cf222e; + cursor: pointer; + font-weight: bold; +} +.remove-btn:hover { + background-color: #ffebe9; +} +.no-bots { + font-size: 12px; + color: #57606a; + text-align: center; + padding: 12px; + font-style: italic; +} + +#status { + margin-top: 10px; + font-size: 12px; + color: #57606a; + text-align: center; + padding: 6px; + border-radius: 4px; +} +.success { + color: #1a7f37; + background-color: #dafbe1; +} +.error { + color: #cf222e; + background-color: #ffebe9; +} +.info { + color: #0969da; + background-color: #ddf4ff; +} diff --git a/src/popup.html b/src/popup.html new file mode 100644 index 0000000..f51c759 --- /dev/null +++ b/src/popup.html @@ -0,0 +1,68 @@ + + + + + + + + +
+

Houdini

+

+ Show + | + Hide + Bot Comments +

+
+ + +
CodeRabbit
+ + +
+
+ All +
+ +
+ + +
+ +
+ + +
+
Other Bots
+ +
+ + +
+ + + + +
+
+ +
+ + + + +
+ + diff --git a/src/popup.ts b/src/popup.ts new file mode 100644 index 0000000..c9d7b99 --- /dev/null +++ b/src/popup.ts @@ -0,0 +1,5 @@ +console.log("Hello, world!") + +import browser from "webextension-polyfill" +const [tab] = await browser.tabs.query({ active: true, currentWindow: true }) +console.log("tab", tab) diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..4feef53 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { "checkJs": true }, + "include": ["eslint.config.ts", "src", "vitest.config.ts", "esbuild.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..19de502 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,64 @@ +{ + "compilerOptions": { + /* Projects */ + "incremental": true, + "composite": true, + + /* Language and Environment */ + "target": "ESNext", + "lib": ["DOM", "ESNext"], + "useDefineForClassFields": true, + "moduleDetection": "force", + + /* Modules */ + "module": "NodeNext", + "rootDir": "./src", + "moduleResolution": "NodeNext", + "typeRoots": ["./node_modules/@types", "./src/types"], + "rewriteRelativeImportExtensions": true, + "resolvePackageJsonExports": true, + "resolvePackageJsonImports": true, + "resolveJsonModule": true, + + /* JavaScript Support */ + + /* Emit */ + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + + /* Interop Constraints */ + "isolatedModules": true, + "verbatimModuleSyntax": true, + "isolatedDeclarations": true, + "erasableSyntaxOnly": true, + "forceConsistentCasingInFileNames": true, + + /* Type Checking */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "useUnknownInCatchVariables": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + + /* Completeness */ + "skipDefaultLibCheck": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..fa4341d --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +import type { ViteUserConfig } from "vitest/config" +import { defineConfig } from "vitest/config" + +const config: ViteUserConfig = defineConfig({ + test: { + include: ["src/**/*.test.ts"], + coverage: { + include: ["src/**/*.ts"], + reporter: ["html-spa", "json-summary", "text"], + }, + }, +}) + +export default config From 1e3cc9c6f6ab5c5ba1b488ad01e83d1cadd7a0fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:54:12 +0000 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=93=8C=20pnpm=20install=20--fix-loc?= =?UTF-8?q?kfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [dependabot skip] --- pnpm-lock.yaml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d577deb..a754fcd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -403,79 +403,66 @@ packages: resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.55.1': resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.55.1': resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.55.1': resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.55.1': resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.55.1': resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.55.1': resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.55.1': resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.55.1': resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.55.1': resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.55.1': resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.55.1': resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.55.1': resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.55.1': resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} @@ -2176,7 +2163,6 @@ packages: whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} From 3de028f5afe40bf802c9548b608c2beb124528aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:54:16 +0000 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=8E=A8=20pnpm=20run=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [dependabot skip] --- houdini/manifest.json | 46 +- houdini/popup.html | 586 ++++++++-------- houdini/popup.js | 1532 +++++++++++++++++++++-------------------- 3 files changed, 1105 insertions(+), 1059 deletions(-) diff --git a/houdini/manifest.json b/houdini/manifest.json index da214fc..ade19da 100644 --- a/houdini/manifest.json +++ b/houdini/manifest.json @@ -1,27 +1,21 @@ { - "manifest_version": 3, - "name": "Houdini", - "version": "1.0", - "description": "Show or hide bot comments on GitHub PRs", - "permissions": [ - "activeTab", - "scripting", - "storage" - ], - "host_permissions": [ - "https://github.com/*" - ], - "action": { - "default_popup": "popup.html", - "default_icon": { - "16": "icon16.png", - "48": "icon48.png", - "128": "icon128.png" - } - }, - "icons": { - "16": "icon16.png", - "48": "icon48.png", - "128": "icon128.png" - } -} \ No newline at end of file + "manifest_version": 3, + "name": "Houdini", + "version": "1.0", + "description": "Show or hide bot comments on GitHub PRs", + "permissions": ["activeTab", "scripting", "storage"], + "host_permissions": ["https://github.com/*"], + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + } + }, + "icons": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + } +} diff --git a/houdini/popup.html b/houdini/popup.html index c84a5e1..2c87874 100644 --- a/houdini/popup.html +++ b/houdini/popup.html @@ -1,293 +1,295 @@ - + - - - - - -
-

Houdini

-

- Show - | - Hide - Bot Comments -

-
- - -
CodeRabbit
- - -
-
- All -
- -
- - -
- -
- - -
-
Other Bots
- -
- - -
- - - - -
-
- -
- - - - -
- - - \ No newline at end of file + + + + + +
+

Houdini

+

+ Show + | + Hide + Bot Comments +

+
+ + +
CodeRabbit
+ + +
+
+ All +
+ +
+ + +
+ +
+ + +
+
Other Bots
+ +
+ + +
+ + + + +
+
+ +
+ + + + +
+ + + diff --git a/houdini/popup.js b/houdini/popup.js index 6b94da2..043f1ee 100644 --- a/houdini/popup.js +++ b/houdini/popup.js @@ -2,776 +2,826 @@ // CONSTANTS & DEFAULTS // ============================================================================ -const SEVERITIES = ['Critical', 'Major', 'Minor']; +const SEVERITIES = ["Critical", "Major", "Minor"] const DEFAULT_STATE = { - coderabbit: { - visibilityState: { - 'Critical': true, - 'Major': true, - 'Minor': true - }, - showAllState: true - }, - customBots: {} // { "botName": true|false } -}; + coderabbit: { + visibilityState: { + Critical: true, + Major: true, + Minor: true, + }, + showAllState: true, + }, + customBots: {}, // { "botName": true|false } +} // ============================================================================ // STATE MANAGEMENT // ============================================================================ class FilterState { - constructor() { - this.currentState = this.getDefaultState(); - } - - getDefaultState() { - return { - coderabbit: { - visibilityState: { ...DEFAULT_STATE.coderabbit.visibilityState }, - showAllState: DEFAULT_STATE.coderabbit.showAllState - }, - customBots: { ...DEFAULT_STATE.customBots } - }; - } - - validateState(state) { - if (!state || typeof state !== 'object') { - return this.getDefaultState(); - } - - const validated = { - coderabbit: { - visibilityState: {}, - showAllState: state.coderabbit?.showAllState ?? true - }, - customBots: {} - }; - - SEVERITIES.forEach(severity => { - validated.coderabbit.visibilityState[severity] = - state.coderabbit?.visibilityState?.[severity] ?? true; - }); - - if (state.customBots && typeof state.customBots === 'object') { - Object.entries(state.customBots).forEach(([botName, showAll]) => { - if (typeof botName === 'string' && botName.trim()) { - validated.customBots[botName.toLowerCase().trim()] = Boolean(showAll); - } - }); - } - - return validated; - } - - getSessionKey(tabId) { - return `session_${tabId}`; - } - - async load(tabId) { - try { - if (!chrome?.storage) { - console.warn('chrome.storage not available, using defaults'); - this.currentState = this.getDefaultState(); - return this.currentState; - } - - const sessionKey = this.getSessionKey(tabId); - const sessionResult = await chrome.storage.session.get(sessionKey); - - if (sessionResult[sessionKey]) { - console.log('Loading from session storage for tab', tabId); - this.currentState = this.validateState(sessionResult[sessionKey]); - return this.currentState; - } - - const result = await chrome.storage.sync.get(['coderabbit', 'customBots']); - console.log('Loading from global storage:', result); - this.currentState = this.validateState(result); - return this.currentState; - } catch (error) { - console.error('Error loading settings:', error); - this.currentState = this.getDefaultState(); - return this.currentState; - } - } - - async saveToSession(tabId) { - try { - if (!chrome?.storage?.session) { - console.warn('chrome.storage.session not available'); - return; - } - - const sessionKey = this.getSessionKey(tabId); - await chrome.storage.session.set({ - [sessionKey]: { - coderabbit: { - visibilityState: { ...this.currentState.coderabbit.visibilityState }, - showAllState: this.currentState.coderabbit.showAllState - }, - customBots: { ...this.currentState.customBots } - } - }); - console.log('Saved to session storage for tab', tabId); - } catch (error) { - console.error('Error saving to session:', error); - } - } - - async saveAsDefault() { - try { - if (!chrome?.storage?.sync) { - console.error('chrome.storage.sync not available'); - return false; - } - - await chrome.storage.sync.set({ - coderabbit: { - visibilityState: this.currentState.coderabbit.visibilityState, - showAllState: this.currentState.coderabbit.showAllState - }, - customBots: this.currentState.customBots - }); - console.log('Saved as global defaults:', this.currentState); - return true; - } catch (error) { - console.error('Error saving defaults:', error); - return false; - } - } - - setSeverityVisibility(severity, isVisible) { - if (SEVERITIES.includes(severity)) { - this.currentState.coderabbit.visibilityState[severity] = Boolean(isVisible); - } - } - - setShowAll(showAll) { - this.currentState.coderabbit.showAllState = Boolean(showAll); - SEVERITIES.forEach(severity => { - this.currentState.coderabbit.visibilityState[severity] = showAll; - }); - } - - setCustomBot(botName, showAll) { - if (typeof botName === 'string' && botName.trim()) { - const normalizedName = botName.toLowerCase().trim(); - this.currentState.customBots[normalizedName] = Boolean(showAll); - } - } - - removeCustomBot(botName) { - if (typeof botName === 'string') { - const normalizedName = botName.toLowerCase().trim(); - delete this.currentState.customBots[normalizedName]; - } - } - - getCustomBotNames() { - return Object.keys(this.currentState.customBots); - } - - get() { - return { - coderabbit: { - visibilityState: { ...this.currentState.coderabbit.visibilityState }, - showAllState: this.currentState.coderabbit.showAllState - }, - customBots: { ...this.currentState.customBots } - }; - } + constructor() { + this.currentState = this.getDefaultState() + } + + getDefaultState() { + return { + coderabbit: { + visibilityState: { ...DEFAULT_STATE.coderabbit.visibilityState }, + showAllState: DEFAULT_STATE.coderabbit.showAllState, + }, + customBots: { ...DEFAULT_STATE.customBots }, + } + } + + validateState(state) { + if (!state || typeof state !== "object") { + return this.getDefaultState() + } + + const validated = { + coderabbit: { + visibilityState: {}, + showAllState: state.coderabbit?.showAllState ?? true, + }, + customBots: {}, + } + + SEVERITIES.forEach(severity => { + validated.coderabbit.visibilityState[severity] = + state.coderabbit?.visibilityState?.[severity] ?? true + }) + + if (state.customBots && typeof state.customBots === "object") { + Object.entries(state.customBots).forEach(([botName, showAll]) => { + if (typeof botName === "string" && botName.trim()) { + validated.customBots[botName.toLowerCase().trim()] = Boolean(showAll) + } + }) + } + + return validated + } + + getSessionKey(tabId) { + return `session_${tabId}` + } + + async load(tabId) { + try { + if (!chrome?.storage) { + console.warn("chrome.storage not available, using defaults") + this.currentState = this.getDefaultState() + return this.currentState + } + + const sessionKey = this.getSessionKey(tabId) + const sessionResult = await chrome.storage.session.get(sessionKey) + + if (sessionResult[sessionKey]) { + console.log("Loading from session storage for tab", tabId) + this.currentState = this.validateState(sessionResult[sessionKey]) + return this.currentState + } + + const result = await chrome.storage.sync.get(["coderabbit", "customBots"]) + console.log("Loading from global storage:", result) + this.currentState = this.validateState(result) + return this.currentState + } catch (error) { + console.error("Error loading settings:", error) + this.currentState = this.getDefaultState() + return this.currentState + } + } + + async saveToSession(tabId) { + try { + if (!chrome?.storage?.session) { + console.warn("chrome.storage.session not available") + return + } + + const sessionKey = this.getSessionKey(tabId) + await chrome.storage.session.set({ + [sessionKey]: { + coderabbit: { + visibilityState: { + ...this.currentState.coderabbit.visibilityState, + }, + showAllState: this.currentState.coderabbit.showAllState, + }, + customBots: { ...this.currentState.customBots }, + }, + }) + console.log("Saved to session storage for tab", tabId) + } catch (error) { + console.error("Error saving to session:", error) + } + } + + async saveAsDefault() { + try { + if (!chrome?.storage?.sync) { + console.error("chrome.storage.sync not available") + return false + } + + await chrome.storage.sync.set({ + coderabbit: { + visibilityState: this.currentState.coderabbit.visibilityState, + showAllState: this.currentState.coderabbit.showAllState, + }, + customBots: this.currentState.customBots, + }) + console.log("Saved as global defaults:", this.currentState) + return true + } catch (error) { + console.error("Error saving defaults:", error) + return false + } + } + + setSeverityVisibility(severity, isVisible) { + if (SEVERITIES.includes(severity)) { + this.currentState.coderabbit.visibilityState[severity] = + Boolean(isVisible) + } + } + + setShowAll(showAll) { + this.currentState.coderabbit.showAllState = Boolean(showAll) + SEVERITIES.forEach(severity => { + this.currentState.coderabbit.visibilityState[severity] = showAll + }) + } + + setCustomBot(botName, showAll) { + if (typeof botName === "string" && botName.trim()) { + const normalizedName = botName.toLowerCase().trim() + this.currentState.customBots[normalizedName] = Boolean(showAll) + } + } + + removeCustomBot(botName) { + if (typeof botName === "string") { + const normalizedName = botName.toLowerCase().trim() + delete this.currentState.customBots[normalizedName] + } + } + + getCustomBotNames() { + return Object.keys(this.currentState.customBots) + } + + get() { + return { + coderabbit: { + visibilityState: { ...this.currentState.coderabbit.visibilityState }, + showAllState: this.currentState.coderabbit.showAllState, + }, + customBots: { ...this.currentState.customBots }, + } + } } -const filterState = new FilterState(); +const filterState = new FilterState() // ============================================================================ // UI CONTROLLER // ============================================================================ class UIController { - constructor() { - this.severityToggles = {}; - this.currentTabId = null; - } - - async init() { - try { - const tab = await this.getCurrentTab(); - - if (!this.validateTab(tab)) { - return; - } - - this.currentTabId = tab.id; - await filterState.load(tab.id); - this.updateCodeRabbitAllToggle(); - this.updateCustomBotUI(); - - const severities = await this.scanSeverities(tab.id); - this.buildSeverityControls(severities); - this.setupEventListeners(); - await this.applyFilters(); - - } catch (error) { - console.error('Error initializing popup:', error); - this.showStatus('Error initializing extension: ' + error.message, 'error'); - } - } - - setupEventListeners() { - const coderabbitAllCheckbox = document.getElementById('coderabbitAllCheckbox'); - if (coderabbitAllCheckbox) { - coderabbitAllCheckbox.onchange = (e) => this.handleCodeRabbitAllToggle(e.target.checked); - } - - const customBotAllCheckbox = document.getElementById('customBotAllCheckbox'); - if (customBotAllCheckbox) { - customBotAllCheckbox.onchange = (e) => this.handleCustomBotAllToggle(e.target.checked); - } - - const addBotBtn = document.getElementById('addBotBtn'); - if (addBotBtn) { - addBotBtn.onclick = () => this.handleAddCustomBots(); - } - - const botNameInput = document.getElementById('botNameInput'); - if (botNameInput) { - botNameInput.onkeypress = (e) => { - if (e.key === 'Enter') this.handleAddCustomBots(); - }; - } - - const saveAsDefaultBtn = document.getElementById('saveAsDefaultBtn'); - if (saveAsDefaultBtn) { - saveAsDefaultBtn.onclick = () => this.handleSaveAsDefault(); - } - } - - async getCurrentTab() { - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - return tab; - } - - validateTab(tab) { - if (!tab) { - this.showStatus('Error: No active tab found', 'error'); - return false; - } - - if (!tab.url || !tab.url.includes('github.com')) { - this.showStatus('Please open a GitHub PR page', 'error'); - return false; - } - - if (!tab.id) { - this.showStatus('Error: Invalid tab ID', 'error'); - return false; - } - - return true; - } - - async scanSeverities(tabId) { - return new Promise((resolve) => { - chrome.scripting.executeScript({ - target: { tabId }, - function: getAvailableSeverities - }, (results) => { - if (chrome.runtime.lastError) { - console.error('Error scanning severities:', chrome.runtime.lastError); - resolve({}); - return; - } - - if (!results?.[0]?.result) { - console.error('Invalid scan results:', results); - resolve({}); - return; - } - - resolve(results[0].result); - }); - }); - } - - buildSeverityControls(availableSeverities) { - const container = document.getElementById('severityControls'); - if (!container) { - console.error('Severity controls container not found'); - return; - } - - container.innerHTML = ''; - const state = filterState.get(); - - SEVERITIES.forEach(severity => { - const count = availableSeverities[severity] || 0; - if (count === 0) return; - - const item = this.createSeverityFilterItem(severity, count, state.coderabbit.visibilityState[severity]); - container.appendChild(item); - }); - } - - createSeverityFilterItem(severity, count, isVisible) { - const item = document.createElement('div'); - item.className = 'filter-item'; - - const label = document.createElement('div'); - label.className = 'severity-label'; - label.innerHTML = ` + constructor() { + this.severityToggles = {} + this.currentTabId = null + } + + async init() { + try { + const tab = await this.getCurrentTab() + + if (!this.validateTab(tab)) { + return + } + + this.currentTabId = tab.id + await filterState.load(tab.id) + this.updateCodeRabbitAllToggle() + this.updateCustomBotUI() + + const severities = await this.scanSeverities(tab.id) + this.buildSeverityControls(severities) + this.setupEventListeners() + await this.applyFilters() + } catch (error) { + console.error("Error initializing popup:", error) + this.showStatus("Error initializing extension: " + error.message, "error") + } + } + + setupEventListeners() { + const coderabbitAllCheckbox = document.getElementById( + "coderabbitAllCheckbox", + ) + if (coderabbitAllCheckbox) { + coderabbitAllCheckbox.onchange = e => + this.handleCodeRabbitAllToggle(e.target.checked) + } + + const customBotAllCheckbox = document.getElementById("customBotAllCheckbox") + if (customBotAllCheckbox) { + customBotAllCheckbox.onchange = e => + this.handleCustomBotAllToggle(e.target.checked) + } + + const addBotBtn = document.getElementById("addBotBtn") + if (addBotBtn) { + addBotBtn.onclick = () => this.handleAddCustomBots() + } + + const botNameInput = document.getElementById("botNameInput") + if (botNameInput) { + botNameInput.onkeypress = e => { + if (e.key === "Enter") this.handleAddCustomBots() + } + } + + const saveAsDefaultBtn = document.getElementById("saveAsDefaultBtn") + if (saveAsDefaultBtn) { + saveAsDefaultBtn.onclick = () => this.handleSaveAsDefault() + } + } + + async getCurrentTab() { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }) + return tab + } + + validateTab(tab) { + if (!tab) { + this.showStatus("Error: No active tab found", "error") + return false + } + + if (!tab.url || !tab.url.includes("github.com")) { + this.showStatus("Please open a GitHub PR page", "error") + return false + } + + if (!tab.id) { + this.showStatus("Error: Invalid tab ID", "error") + return false + } + + return true + } + + async scanSeverities(tabId) { + return new Promise(resolve => { + chrome.scripting.executeScript( + { + target: { tabId }, + function: getAvailableSeverities, + }, + results => { + if (chrome.runtime.lastError) { + console.error( + "Error scanning severities:", + chrome.runtime.lastError, + ) + resolve({}) + return + } + + if (!results?.[0]?.result) { + console.error("Invalid scan results:", results) + resolve({}) + return + } + + resolve(results[0].result) + }, + ) + }) + } + + buildSeverityControls(availableSeverities) { + const container = document.getElementById("severityControls") + if (!container) { + console.error("Severity controls container not found") + return + } + + container.innerHTML = "" + const state = filterState.get() + + SEVERITIES.forEach(severity => { + const count = availableSeverities[severity] || 0 + if (count === 0) return + + const item = this.createSeverityFilterItem( + severity, + count, + state.coderabbit.visibilityState[severity], + ) + container.appendChild(item) + }) + } + + createSeverityFilterItem(severity, count, isVisible) { + const item = document.createElement("div") + item.className = "filter-item" + + const label = document.createElement("div") + label.className = "severity-label" + label.innerHTML = ` ${severity} (${count}) - `; - - const toggle = document.createElement('label'); - toggle.className = 'toggle-switch'; - - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.checked = isVisible; - checkbox.onchange = (e) => this.handleSeverityToggle(severity, e.target.checked); - - const slider = document.createElement('span'); - slider.className = 'slider'; - - toggle.appendChild(checkbox); - toggle.appendChild(slider); - item.appendChild(label); - item.appendChild(toggle); - - this.severityToggles[severity] = checkbox; - return item; - } - - async handleCodeRabbitAllToggle(isChecked) { - try { - filterState.setShowAll(isChecked); - Object.values(this.severityToggles).forEach(checkbox => { - checkbox.checked = isChecked; - }); - await this.applyFilters(); - } catch (error) { - console.error('Error toggling CodeRabbit all:', error); - this.showStatus('Error toggling visibility', 'error'); - } - } - - async handleSeverityToggle(severity, isVisible) { - try { - filterState.setSeverityVisibility(severity, isVisible); - await this.applyFilters(); - } catch (error) { - console.error('Error toggling severity:', error); - this.showStatus('Error toggling visibility', 'error'); - } - } - - updateCodeRabbitAllToggle() { - const state = filterState.get(); - const checkbox = document.getElementById('coderabbitAllCheckbox'); - if (checkbox) { - checkbox.checked = state.coderabbit.showAllState; - } - } - - updateCustomBotUI() { - const state = filterState.get(); - const listContainer = document.getElementById('customBotList'); - const allToggleContainer = document.getElementById('customBotAllToggle'); - - if (!listContainer) return; - - listContainer.innerHTML = ''; - const botNames = Object.keys(state.customBots); - - if (allToggleContainer) { - if (botNames.length === 0) { - allToggleContainer.style.display = 'none'; - } else { - allToggleContainer.style.display = ''; - this.updateCustomBotAllToggle(); - } - } - - if (botNames.length === 0) { - listContainer.innerHTML = '
No custom bot filters
'; - return; - } - - botNames.forEach(botName => { - const showAll = state.customBots[botName]; - const item = this.createCustomBotItem(botName, showAll); - listContainer.appendChild(item); - }); - } - - updateCustomBotAllToggle() { - const state = filterState.get(); - const botNames = Object.keys(state.customBots); - - if (botNames.length === 0) return; - - const allShown = botNames.every(name => state.customBots[name] === true); - const checkbox = document.getElementById('customBotAllCheckbox'); - if (checkbox) { - checkbox.checked = allShown; - } - } - - createCustomBotItem(botName, showAll) { - const item = document.createElement('div'); - item.className = 'custom-bot-item'; - - const removeBtn = document.createElement('button'); - removeBtn.className = 'remove-btn'; - removeBtn.textContent = '×'; - removeBtn.title = 'Remove filter'; - removeBtn.onclick = () => this.handleRemoveCustomBot(botName); - - const nameLabel = document.createElement('div'); - nameLabel.className = 'bot-name'; - nameLabel.textContent = botName; - - const toggle = document.createElement('label'); - toggle.className = 'toggle-switch'; - - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.checked = showAll; - checkbox.onchange = (e) => this.handleCustomBotToggle(botName, e.target.checked); - - const slider = document.createElement('span'); - slider.className = 'slider'; - - toggle.appendChild(checkbox); - toggle.appendChild(slider); - - item.appendChild(removeBtn); - item.appendChild(nameLabel); - item.appendChild(toggle); - - return item; - } - - async handleCustomBotAllToggle(isChecked) { - const botNames = filterState.getCustomBotNames(); - if (botNames.length === 0) { - this.showStatus('No custom bot filters', 'info'); - return; - } - - botNames.forEach(botName => { - filterState.setCustomBot(botName, isChecked); - }); - - this.updateCustomBotUI(); - await this.applyFilters(); - } - - async handleAddCustomBots() { - const input = document.getElementById('botNameInput'); - if (!input) return; - - const rawInput = input.value.trim(); - if (!rawInput) { - this.showStatus('Please enter bot name(s)', 'error'); - return; - } - - const botNames = rawInput.split(',') - .map(name => name.trim().toLowerCase()) - .filter(name => name.length > 0); - - if (botNames.length === 0) { - this.showStatus('Please enter valid bot name(s)', 'error'); - return; - } - - const state = filterState.get(); - const newBots = botNames.filter(name => !(name in state.customBots)); - - if (newBots.length === 0) { - this.showStatus('Bot(s) already filtered', 'info'); - return; - } - - newBots.forEach(botName => { - filterState.setCustomBot(botName, false); - }); - - input.value = ''; - this.updateCustomBotUI(); - await this.applyFilters(); - this.showStatus(`Added ${newBots.length} bot filter(s)`, 'success'); - } - - async handleCustomBotToggle(botName, showAll) { - try { - filterState.setCustomBot(botName, showAll); - this.updateCustomBotAllToggle(); - await this.applyFilters(); - } catch (error) { - console.error('Error toggling custom bot:', error); - this.showStatus('Error toggling bot visibility', 'error'); - } - } - - async handleRemoveCustomBot(botName) { - try { - filterState.removeCustomBot(botName); - this.updateCustomBotUI(); - await this.applyFilters(); - this.showStatus(`Removed filter for ${botName}`, 'success'); - } catch (error) { - console.error('Error removing custom bot:', error); - this.showStatus('Error removing bot filter', 'error'); - } - } - - async applyFilters() { - if (!this.currentTabId) { - console.error('No tab ID available'); - return; - } - - try { - await filterState.saveToSession(this.currentTabId); - const state = filterState.get(); - - chrome.scripting.executeScript({ - target: { tabId: this.currentTabId }, - function: applyVisibilityFilter, - args: [state.coderabbit.visibilityState, state.coderabbit.showAllState, state.customBots] - }, (results) => { - if (chrome.runtime.lastError) { - console.error('Error applying filter:', chrome.runtime.lastError); - this.showStatus('Error applying filter', 'error'); - } - }); - } catch (error) { - console.error('Error in applyFilters:', error); - this.showStatus('Error applying filters', 'error'); - } - } - - async handleSaveAsDefault() { - const success = await filterState.saveAsDefault(); - if (success) { - this.showStatus('Saved as default settings', 'success'); - } else { - this.showStatus('Error saving defaults', 'error'); - } - } - - showStatus(message, type) { - try { - const status = document.getElementById('status'); - if (!status) return; - - status.textContent = message || ''; - status.className = type || ''; - - if (message) { - setTimeout(() => { - status.textContent = ''; - status.className = ''; - }, 3000); - } - } catch (error) { - console.error('Error showing status:', error); - } - } + ` + + const toggle = document.createElement("label") + toggle.className = "toggle-switch" + + const checkbox = document.createElement("input") + checkbox.type = "checkbox" + checkbox.checked = isVisible + checkbox.onchange = e => + this.handleSeverityToggle(severity, e.target.checked) + + const slider = document.createElement("span") + slider.className = "slider" + + toggle.appendChild(checkbox) + toggle.appendChild(slider) + item.appendChild(label) + item.appendChild(toggle) + + this.severityToggles[severity] = checkbox + return item + } + + async handleCodeRabbitAllToggle(isChecked) { + try { + filterState.setShowAll(isChecked) + Object.values(this.severityToggles).forEach(checkbox => { + checkbox.checked = isChecked + }) + await this.applyFilters() + } catch (error) { + console.error("Error toggling CodeRabbit all:", error) + this.showStatus("Error toggling visibility", "error") + } + } + + async handleSeverityToggle(severity, isVisible) { + try { + filterState.setSeverityVisibility(severity, isVisible) + await this.applyFilters() + } catch (error) { + console.error("Error toggling severity:", error) + this.showStatus("Error toggling visibility", "error") + } + } + + updateCodeRabbitAllToggle() { + const state = filterState.get() + const checkbox = document.getElementById("coderabbitAllCheckbox") + if (checkbox) { + checkbox.checked = state.coderabbit.showAllState + } + } + + updateCustomBotUI() { + const state = filterState.get() + const listContainer = document.getElementById("customBotList") + const allToggleContainer = document.getElementById("customBotAllToggle") + + if (!listContainer) return + + listContainer.innerHTML = "" + const botNames = Object.keys(state.customBots) + + if (allToggleContainer) { + if (botNames.length === 0) { + allToggleContainer.style.display = "none" + } else { + allToggleContainer.style.display = "" + this.updateCustomBotAllToggle() + } + } + + if (botNames.length === 0) { + listContainer.innerHTML = + '
No custom bot filters
' + return + } + + botNames.forEach(botName => { + const showAll = state.customBots[botName] + const item = this.createCustomBotItem(botName, showAll) + listContainer.appendChild(item) + }) + } + + updateCustomBotAllToggle() { + const state = filterState.get() + const botNames = Object.keys(state.customBots) + + if (botNames.length === 0) return + + const allShown = botNames.every(name => state.customBots[name] === true) + const checkbox = document.getElementById("customBotAllCheckbox") + if (checkbox) { + checkbox.checked = allShown + } + } + + createCustomBotItem(botName, showAll) { + const item = document.createElement("div") + item.className = "custom-bot-item" + + const removeBtn = document.createElement("button") + removeBtn.className = "remove-btn" + removeBtn.textContent = "×" + removeBtn.title = "Remove filter" + removeBtn.onclick = () => this.handleRemoveCustomBot(botName) + + const nameLabel = document.createElement("div") + nameLabel.className = "bot-name" + nameLabel.textContent = botName + + const toggle = document.createElement("label") + toggle.className = "toggle-switch" + + const checkbox = document.createElement("input") + checkbox.type = "checkbox" + checkbox.checked = showAll + checkbox.onchange = e => + this.handleCustomBotToggle(botName, e.target.checked) + + const slider = document.createElement("span") + slider.className = "slider" + + toggle.appendChild(checkbox) + toggle.appendChild(slider) + + item.appendChild(removeBtn) + item.appendChild(nameLabel) + item.appendChild(toggle) + + return item + } + + async handleCustomBotAllToggle(isChecked) { + const botNames = filterState.getCustomBotNames() + if (botNames.length === 0) { + this.showStatus("No custom bot filters", "info") + return + } + + botNames.forEach(botName => { + filterState.setCustomBot(botName, isChecked) + }) + + this.updateCustomBotUI() + await this.applyFilters() + } + + async handleAddCustomBots() { + const input = document.getElementById("botNameInput") + if (!input) return + + const rawInput = input.value.trim() + if (!rawInput) { + this.showStatus("Please enter bot name(s)", "error") + return + } + + const botNames = rawInput + .split(",") + .map(name => name.trim().toLowerCase()) + .filter(name => name.length > 0) + + if (botNames.length === 0) { + this.showStatus("Please enter valid bot name(s)", "error") + return + } + + const state = filterState.get() + const newBots = botNames.filter(name => !(name in state.customBots)) + + if (newBots.length === 0) { + this.showStatus("Bot(s) already filtered", "info") + return + } + + newBots.forEach(botName => { + filterState.setCustomBot(botName, false) + }) + + input.value = "" + this.updateCustomBotUI() + await this.applyFilters() + this.showStatus(`Added ${newBots.length} bot filter(s)`, "success") + } + + async handleCustomBotToggle(botName, showAll) { + try { + filterState.setCustomBot(botName, showAll) + this.updateCustomBotAllToggle() + await this.applyFilters() + } catch (error) { + console.error("Error toggling custom bot:", error) + this.showStatus("Error toggling bot visibility", "error") + } + } + + async handleRemoveCustomBot(botName) { + try { + filterState.removeCustomBot(botName) + this.updateCustomBotUI() + await this.applyFilters() + this.showStatus(`Removed filter for ${botName}`, "success") + } catch (error) { + console.error("Error removing custom bot:", error) + this.showStatus("Error removing bot filter", "error") + } + } + + async applyFilters() { + if (!this.currentTabId) { + console.error("No tab ID available") + return + } + + try { + await filterState.saveToSession(this.currentTabId) + const state = filterState.get() + + chrome.scripting.executeScript( + { + target: { tabId: this.currentTabId }, + function: applyVisibilityFilter, + args: [ + state.coderabbit.visibilityState, + state.coderabbit.showAllState, + state.customBots, + ], + }, + results => { + if (chrome.runtime.lastError) { + console.error("Error applying filter:", chrome.runtime.lastError) + this.showStatus("Error applying filter", "error") + } + }, + ) + } catch (error) { + console.error("Error in applyFilters:", error) + this.showStatus("Error applying filters", "error") + } + } + + async handleSaveAsDefault() { + const success = await filterState.saveAsDefault() + if (success) { + this.showStatus("Saved as default settings", "success") + } else { + this.showStatus("Error saving defaults", "error") + } + } + + showStatus(message, type) { + try { + const status = document.getElementById("status") + if (!status) return + + status.textContent = message || "" + status.className = type || "" + + if (message) { + setTimeout(() => { + status.textContent = "" + status.className = "" + }, 3000) + } + } catch (error) { + console.error("Error showing status:", error) + } + } } // ============================================================================ // INITIALIZATION // ============================================================================ -const ui = new UIController(); -document.addEventListener('DOMContentLoaded', () => ui.init()); +const ui = new UIController() +document.addEventListener("DOMContentLoaded", () => ui.init()) // ============================================================================ // INJECTED FUNCTIONS (executed in page context) // ============================================================================ function getAvailableSeverities() { - try { - const comments = document.querySelectorAll('turbo-frame[id^="review-thread-or-comment-id-"]'); - const severityCounts = {}; - - comments.forEach(comment => { - try { - const authorLink = comment.querySelector('a.author, a[data-hovercard-type="user"]'); - - if (authorLink && authorLink.textContent.trim().toLowerCase().includes('coderabbit')) { - const commentBody = comment.querySelector('.comment-body'); - if (!commentBody) return; - - const allEms = commentBody.querySelectorAll('em'); - - for (const em of allEms) { - const text = em.textContent.trim(); - let severity = null; - - if (text.includes('Critical')) severity = 'Critical'; - else if (text.includes('Major')) severity = 'Major'; - else if (text.includes('Minor')) severity = 'Minor'; - - if (severity) { - severityCounts[severity] = (severityCounts[severity] || 0) + 1; - break; - } - } - } - } catch (err) { - console.error('Error processing comment:', err); - } - }); - - return severityCounts; - } catch (error) { - console.error('Error in getAvailableSeverities:', error); - return {}; - } + try { + const comments = document.querySelectorAll( + 'turbo-frame[id^="review-thread-or-comment-id-"]', + ) + const severityCounts = {} + + comments.forEach(comment => { + try { + const authorLink = comment.querySelector( + 'a.author, a[data-hovercard-type="user"]', + ) + + if ( + authorLink && + authorLink.textContent.trim().toLowerCase().includes("coderabbit") + ) { + const commentBody = comment.querySelector(".comment-body") + if (!commentBody) return + + const allEms = commentBody.querySelectorAll("em") + + for (const em of allEms) { + const text = em.textContent.trim() + let severity = null + + if (text.includes("Critical")) severity = "Critical" + else if (text.includes("Major")) severity = "Major" + else if (text.includes("Minor")) severity = "Minor" + + if (severity) { + severityCounts[severity] = (severityCounts[severity] || 0) + 1 + break + } + } + } + } catch (err) { + console.error("Error processing comment:", err) + } + }) + + return severityCounts + } catch (error) { + console.error("Error in getAvailableSeverities:", error) + return {} + } } -function applyVisibilityFilter(coderabbitVisibilityState, coderabbitShowAllState, customBots) { - const SELECTORS = { - TIMELINE_ITEM: '.js-timeline-item', - TURBO_FRAME: 'turbo-frame[id^="review-thread-or-comment-id-"]', - INLINE_CONTAINER: '.js-inline-comments-container', - COMMENT_BODY: '.comment-body', - CODERABBIT_AUTHOR_LINK: 'a.author[href="/apps/coderabbitai"]', - AUTHOR_LINK: 'a.author', - SEVERITY_EM: 'em' - }; - - try { - const allTimelineItems = document.querySelectorAll(SELECTORS.TIMELINE_ITEM); - - function getTurboFrameSeverity(turboFrame) { - try { - const inlineContainers = turboFrame.querySelectorAll(SELECTORS.INLINE_CONTAINER); - - for (const inlineContainer of inlineContainers) { - const authorLink = inlineContainer.querySelector(SELECTORS.CODERABBIT_AUTHOR_LINK); - - if (authorLink) { - const commentBody = inlineContainer.querySelector(SELECTORS.COMMENT_BODY); - if (!commentBody) continue; - - const allEms = commentBody.querySelectorAll(SELECTORS.SEVERITY_EM); - for (const em of allEms) { - const text = em.textContent.trim(); - if (text.includes('Critical')) return 'Critical'; - if (text.includes('Major')) return 'Major'; - if (text.includes('Minor')) return 'Minor'; - } - } - } - return null; - } catch (error) { - console.error('Error detecting turbo-frame severity:', error); - return null; - } - } - - function isCustomBotHidden(timelineItem) { - if (!customBots || Object.keys(customBots).length === 0) return false; - - const authorLinks = timelineItem.querySelectorAll(SELECTORS.AUTHOR_LINK); - - for (const authorLink of authorLinks) { - const authorName = authorLink.textContent.trim().toLowerCase(); - - for (const [botName, showAll] of Object.entries(customBots)) { - if (authorName.includes(botName.toLowerCase())) { - return !showAll; - } - } - } - - return false; - } - - function isCodeRabbit(container) { - return !!container.querySelector(SELECTORS.CODERABBIT_AUTHOR_LINK); - } - - allTimelineItems.forEach(container => { - try { - const shouldHideCustomBot = isCustomBotHidden(container); - - if (shouldHideCustomBot) { - container.style.display = 'none'; - container.setAttribute('data-custom-bot-hidden', 'true'); - return; - } else { - container.removeAttribute('data-custom-bot-hidden'); - } - - const isCodeRabbitComment = isCodeRabbit(container); - - if (isCodeRabbitComment) { - const turboFrames = container.querySelectorAll(SELECTORS.TURBO_FRAME); - - if (coderabbitShowAllState) { - container.style.display = ''; - container.removeAttribute('data-coderabbit-hidden'); - - turboFrames.forEach(turboFrame => { - try { - const severity = getTurboFrameSeverity(turboFrame); - - if (severity && coderabbitVisibilityState[severity] === false) { - turboFrame.style.display = 'none'; - turboFrame.setAttribute('data-coderabbit-hidden', 'true'); - } else { - turboFrame.style.display = ''; - turboFrame.removeAttribute('data-coderabbit-hidden'); - } - } catch (error) { - console.error('Error processing turbo-frame in show-all mode:', error); - } - }); - } else { - container.style.display = ''; - container.removeAttribute('data-coderabbit-hidden'); - - let hasVisibleTurboFrame = false; - - turboFrames.forEach(turboFrame => { - try { - const severity = getTurboFrameSeverity(turboFrame); - - if (severity && coderabbitVisibilityState[severity] === true) { - turboFrame.style.display = ''; - turboFrame.removeAttribute('data-coderabbit-hidden'); - hasVisibleTurboFrame = true; - } else { - turboFrame.style.display = 'none'; - turboFrame.setAttribute('data-coderabbit-hidden', 'true'); - } - } catch (error) { - console.error('Error processing turbo-frame in hide-all mode:', error); - } - }); - - if (!hasVisibleTurboFrame) { - container.style.display = 'none'; - container.setAttribute('data-coderabbit-hidden', 'true'); - } - } - } else { - container.style.display = ''; - } - } catch (error) { - console.error('Error processing timeline item:', error); - } - }); - } catch (error) { - console.error('Error in applyVisibilityFilter:', error); - } -} \ No newline at end of file +function applyVisibilityFilter( + coderabbitVisibilityState, + coderabbitShowAllState, + customBots, +) { + const SELECTORS = { + TIMELINE_ITEM: ".js-timeline-item", + TURBO_FRAME: 'turbo-frame[id^="review-thread-or-comment-id-"]', + INLINE_CONTAINER: ".js-inline-comments-container", + COMMENT_BODY: ".comment-body", + CODERABBIT_AUTHOR_LINK: 'a.author[href="/apps/coderabbitai"]', + AUTHOR_LINK: "a.author", + SEVERITY_EM: "em", + } + + try { + const allTimelineItems = document.querySelectorAll(SELECTORS.TIMELINE_ITEM) + + function getTurboFrameSeverity(turboFrame) { + try { + const inlineContainers = turboFrame.querySelectorAll( + SELECTORS.INLINE_CONTAINER, + ) + + for (const inlineContainer of inlineContainers) { + const authorLink = inlineContainer.querySelector( + SELECTORS.CODERABBIT_AUTHOR_LINK, + ) + + if (authorLink) { + const commentBody = inlineContainer.querySelector( + SELECTORS.COMMENT_BODY, + ) + if (!commentBody) continue + + const allEms = commentBody.querySelectorAll(SELECTORS.SEVERITY_EM) + for (const em of allEms) { + const text = em.textContent.trim() + if (text.includes("Critical")) return "Critical" + if (text.includes("Major")) return "Major" + if (text.includes("Minor")) return "Minor" + } + } + } + return null + } catch (error) { + console.error("Error detecting turbo-frame severity:", error) + return null + } + } + + function isCustomBotHidden(timelineItem) { + if (!customBots || Object.keys(customBots).length === 0) return false + + const authorLinks = timelineItem.querySelectorAll(SELECTORS.AUTHOR_LINK) + + for (const authorLink of authorLinks) { + const authorName = authorLink.textContent.trim().toLowerCase() + + for (const [botName, showAll] of Object.entries(customBots)) { + if (authorName.includes(botName.toLowerCase())) { + return !showAll + } + } + } + + return false + } + + function isCodeRabbit(container) { + return !!container.querySelector(SELECTORS.CODERABBIT_AUTHOR_LINK) + } + + allTimelineItems.forEach(container => { + try { + const shouldHideCustomBot = isCustomBotHidden(container) + + if (shouldHideCustomBot) { + container.style.display = "none" + container.setAttribute("data-custom-bot-hidden", "true") + return + } else { + container.removeAttribute("data-custom-bot-hidden") + } + + const isCodeRabbitComment = isCodeRabbit(container) + + if (isCodeRabbitComment) { + const turboFrames = container.querySelectorAll(SELECTORS.TURBO_FRAME) + + if (coderabbitShowAllState) { + container.style.display = "" + container.removeAttribute("data-coderabbit-hidden") + + turboFrames.forEach(turboFrame => { + try { + const severity = getTurboFrameSeverity(turboFrame) + + if (severity && coderabbitVisibilityState[severity] === false) { + turboFrame.style.display = "none" + turboFrame.setAttribute("data-coderabbit-hidden", "true") + } else { + turboFrame.style.display = "" + turboFrame.removeAttribute("data-coderabbit-hidden") + } + } catch (error) { + console.error( + "Error processing turbo-frame in show-all mode:", + error, + ) + } + }) + } else { + container.style.display = "" + container.removeAttribute("data-coderabbit-hidden") + + let hasVisibleTurboFrame = false + + turboFrames.forEach(turboFrame => { + try { + const severity = getTurboFrameSeverity(turboFrame) + + if (severity && coderabbitVisibilityState[severity] === true) { + turboFrame.style.display = "" + turboFrame.removeAttribute("data-coderabbit-hidden") + hasVisibleTurboFrame = true + } else { + turboFrame.style.display = "none" + turboFrame.setAttribute("data-coderabbit-hidden", "true") + } + } catch (error) { + console.error( + "Error processing turbo-frame in hide-all mode:", + error, + ) + } + }) + + if (!hasVisibleTurboFrame) { + container.style.display = "none" + container.setAttribute("data-coderabbit-hidden", "true") + } + } + } else { + container.style.display = "" + } + } catch (error) { + console.error("Error processing timeline item:", error) + } + }) + } catch (error) { + console.error("Error in applyVisibilityFilter:", error) + } +} From 4be06f265af1dcf205f2f111d0d35e8afec5a8c4 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Thu, 22 Jan 2026 19:01:58 -0500 Subject: [PATCH 04/22] =?UTF-8?q?=F0=9F=9A=A8=20Don't=20lint=20old=20exten?= =?UTF-8?q?sion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.ts b/eslint.config.ts index ce95291..f54d083 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -89,6 +89,7 @@ export default defineConfig( "coverage/", "dist/", "docs/", + "houdini/", "node_modules/", "out/", From b527b6ebf64c4cb68d0684c6b155272077845175 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 19:24:25 -0500 Subject: [PATCH 05/22] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20Add=20`CODEOWNERS?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..dbd44b4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +*.ts @coderabbitai/typescript-reviewers From 297a35cd857edbeb2b095c40ab8bd9103f9663b9 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 19:25:05 -0500 Subject: [PATCH 06/22] =?UTF-8?q?=E2=9C=A8=20Add=20severity=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esbuild.ts | 10 ++++- manifest.json | 16 ++++---- package.json | 2 +- src/popup.html | 3 +- src/popup.ts | 56 ++++++++++++++++++++++++++-- src/register.ts | 35 +++++++++++++++++ src/severity.ts | 9 +++++ src/state.ts | 0 src/template.d.ts | 4 ++ src/templates/severity.template.html | 11 ++++++ src/templates/severity.ts | 51 +++++++++++++++++++++++++ 11 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 src/register.ts create mode 100644 src/severity.ts create mode 100644 src/state.ts create mode 100644 src/template.d.ts create mode 100644 src/templates/severity.template.html create mode 100644 src/templates/severity.ts diff --git a/esbuild.ts b/esbuild.ts index 43e132c..6ddf3e9 100644 --- a/esbuild.ts +++ b/esbuild.ts @@ -17,10 +17,16 @@ await esbuild.build({ // limitations under the License.`, }, bundle: true, - entryPoints: ["src/popup.ts"], + entryPoints: ["src/popup.css", "src/popup.html", "src/popup.ts"], footer: { js: "// © 2026 CodeRabbit" }, format: "esm", - loader: { ".ts": "ts" }, + loader: { + ".css": "copy", + ".html": "copy", + ".template.html": "text", + ".ts": "ts", + }, + minify: false, outdir: "out", sourcemap: true, splitting: true, diff --git a/manifest.json b/manifest.json index 4b4b392..bccbf66 100644 --- a/manifest.json +++ b/manifest.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/webextension.json", "manifest_version": 3, "name": "Houdini", "version": "0.0.0", @@ -6,21 +7,20 @@ "permissions": ["activeTab", "scripting", "storage"], "host_permissions": ["https://github.com/*"], "action": { - "default_popup": "src/popup.html", + "default_popup": "out/popup.html", "default_icon": { - "16": "houdini/icon16.png", - "48": "houdini/icon48.png", - "128": "houdini/icon128.png" + "1024": "houdini/icon128.png" } }, "icons": { - "16": "houdini/icon16.png", - "48": "houdini/icon48.png", - "128": "houdini/icon128.png" + "1024": "houdini/icon128.png" }, "browser_specific_settings": { "gecko": { - "id": "@coderabbitai-houdini" + "id": "@coderabbitai-houdini", + "data_collection_permissions": { + "required": ["websiteContent"] + } } } } diff --git a/package.json b/package.json index 2e5d6a0..2978d63 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "dev": "node esbuild.ts && web-ext run --devtools --no-reload", "dev:watch": "node esbuild.ts && web-ext run --devtools", "format": "prettier --write .", - "lint": "web-ext lint && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", + "lint": "web-ext lint --ignore-files houdini && tsgo --noEmit && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", "lint:fix": "eslint --fix --quiet .; markdownlint-cli2 \"**/*.md\" --fix; prettier --list-different --write .", "package": "web-ext build", "start": "node esbuild.ts && web-ext run --no-reload", diff --git a/src/popup.html b/src/popup.html index f51c759..4578439 100644 --- a/src/popup.html +++ b/src/popup.html @@ -3,8 +3,9 @@ - + +

Houdini

diff --git a/src/popup.ts b/src/popup.ts index c9d7b99..86dbc39 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,5 +1,53 @@ -console.log("Hello, world!") +import { register } from "./register.ts" -import browser from "webextension-polyfill" -const [tab] = await browser.tabs.query({ active: true, currentWindow: true }) -console.log("tab", tab) +register( + "#coderabbitAllCheckbox", + HTMLInputElement, + "change", + handleCodeRabbitAllToggle, +) + +function handleCodeRabbitAllToggle(this: HTMLInputElement, ev: Event) { + console.log("checked", this.checked) +} + +register( + "#customBotAllCheckbox", + HTMLInputElement, + "change", + handleCustomBotAllToggle, +) + +function handleCustomBotAllToggle(this: HTMLInputElement, ev: Event) { + console.log("checked", this.checked) +} + +register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBots) + +function handleAddCustomBots(this: HTMLButtonElement, ev: Event) { + console.log("Add Custom Bots clicked") +} + +register("#botNameInput", HTMLInputElement, "keypress", handleAddCustomBots2) + +function handleAddCustomBots2(this: HTMLInputElement, ev: KeyboardEvent) { + if (ev.key !== "Enter") return + console.log("Add Custom Bots clicked via Enter key") +} + +register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) + +function handleSaveAsDefault(this: HTMLButtonElement, ev: Event) { + console.log("Save as Default clicked") +} + +import { severities } from "./severity.ts" +import { htmlSeverity } from "./templates/severity.ts" + +for (const severity of severities) { + const container = document.getElementById("severityControls") + if (!container) throw new Error("severityControls container not found") + + const newControl = htmlSeverity({ severity, count: 0, checked: true }) + container.appendChild(newControl) +} diff --git a/src/register.ts b/src/register.ts new file mode 100644 index 0000000..1798760 --- /dev/null +++ b/src/register.ts @@ -0,0 +1,35 @@ +type Listener = ( + this: E, + ev: HTMLElementEventMap[K], +) => unknown + +function isKEvent( + event: Event, + type: K, +): event is HTMLElementEventMap[K] { + return event.type === type +} + +export function register< + E extends HTMLElement, + K extends keyof HTMLElementEventMap, +>( + selectors: string, + constructor: new () => E, + type: K, + listener: Listener, +): void { + const element = document.querySelector(selectors) + if (!element) throw new Error("Element not found", { cause: { selectors } }) + if (!(element instanceof constructor)) + throw new TypeError("Element is of incorrect type", { + cause: { selectors, constructor }, + }) + + element.addEventListener(type, ev => { + if (!isKEvent(ev, type)) + throw new Error("Event is of incorrect type", { cause: { type, ev } }) + + listener.call(element, ev) + }) +} diff --git a/src/severity.ts b/src/severity.ts new file mode 100644 index 0000000..2390abf --- /dev/null +++ b/src/severity.ts @@ -0,0 +1,9 @@ +export const Severity = { + Critical: "Critical", + Major: "Major", + Minor: "Minor", +} as const + +export type Severity = (typeof Severity)[keyof typeof Severity] + +export const severities: Severity[] = Object.values(Severity) diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/template.d.ts b/src/template.d.ts new file mode 100644 index 0000000..b2fba2c --- /dev/null +++ b/src/template.d.ts @@ -0,0 +1,4 @@ +declare module "*.template.html" { + const content: string + export default content +} diff --git a/src/templates/severity.template.html b/src/templates/severity.template.html new file mode 100644 index 0000000..945d4b9 --- /dev/null +++ b/src/templates/severity.template.html @@ -0,0 +1,11 @@ +
+
+ + +
+ + +
diff --git a/src/templates/severity.ts b/src/templates/severity.ts new file mode 100644 index 0000000..c91c947 --- /dev/null +++ b/src/templates/severity.ts @@ -0,0 +1,51 @@ +import type { Severity } from "../severity.ts" +import html from "./severity.template.html" + +const template = document.createElement("template") +template.innerHTML = html + +interface Props { + readonly severity: Severity + readonly count: number + readonly checked: boolean +} + +export function htmlSeverity(props: Props): HTMLElement { + // 1. Clone the template; this will become our component + const clone = template.content.cloneNode(true) + if (!(clone instanceof DocumentFragment)) + throw new TypeError("cloneNode did not return a DocumentFragment", { + cause: { clone, template }, + }) + + // 2. Extract references from the component + const refs = { + name: clone.querySelector('[data-ref="name"]'), + count: clone.querySelector('[data-ref="count"]'), + checkbox: clone.querySelector('[data-ref="checkbox"]'), + } + if (!refs.name || !refs.count || !refs.checkbox) + throw new Error("Missing required refs", { + cause: { refs, clone }, + }) + + // 3. Setup the component + refs.name.textContent = props.severity + refs.count.textContent = `(${props.count})` + refs.checkbox.checked = props.checked + refs.checkbox.addEventListener("change", toggleSeverity(props.severity)) + + // 4. Extract the HTMLElement from the component and return it + const firstChild = clone.firstChild + if (!(firstChild instanceof HTMLElement)) + throw new TypeError("firstChild is not an HTMLElement", { + cause: { firstChild, clone }, + }) + return firstChild +} + +function toggleSeverity(severity: Severity) { + return (ev: Event) => { + console.log("Toggled severity:", severity, ev) + } +} From 8d328fcb96bd307840b3aff0b3c0e2144fc1b69c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 00:25:48 +0000 Subject: [PATCH 07/22] =?UTF-8?q?=F0=9F=93=8C=20pnpm=20install=20--fix-loc?= =?UTF-8?q?kfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [dependabot skip] --- pnpm-lock.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a754fcd..d4f6683 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -403,66 +403,79 @@ packages: resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.55.1': resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.55.1': resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.55.1': resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.55.1': resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.55.1': resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.55.1': resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.55.1': resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.55.1': resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.55.1': resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.55.1': resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.55.1': resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.55.1': resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.55.1': resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} From 02f970a340156bfab9014c69b5ea81560b49ee4e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 00:25:52 +0000 Subject: [PATCH 08/22] =?UTF-8?q?=F0=9F=92=9A=20ts-autofix=20--project=20t?= =?UTF-8?q?sconfig.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [dependabot skip] --- src/popup.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/popup.ts b/src/popup.ts index 86dbc39..e56d7a9 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -7,7 +7,7 @@ register( handleCodeRabbitAllToggle, ) -function handleCodeRabbitAllToggle(this: HTMLInputElement, ev: Event) { +function handleCodeRabbitAllToggle(this: HTMLInputElement, _ev: Event) { console.log("checked", this.checked) } @@ -18,13 +18,13 @@ register( handleCustomBotAllToggle, ) -function handleCustomBotAllToggle(this: HTMLInputElement, ev: Event) { +function handleCustomBotAllToggle(this: HTMLInputElement, _ev: Event) { console.log("checked", this.checked) } register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBots) -function handleAddCustomBots(this: HTMLButtonElement, ev: Event) { +function handleAddCustomBots(this: HTMLButtonElement, _ev: Event) { console.log("Add Custom Bots clicked") } @@ -37,7 +37,7 @@ function handleAddCustomBots2(this: HTMLInputElement, ev: KeyboardEvent) { register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) -function handleSaveAsDefault(this: HTMLButtonElement, ev: Event) { +function handleSaveAsDefault(this: HTMLButtonElement, _ev: Event) { console.log("Save as Default clicked") } From c7ac0ee5ee142325447026ea62b6de5f2a4714e0 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 20:32:01 -0500 Subject: [PATCH 09/22] =?UTF-8?q?=E2=9C=A8=20Now=20handles=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/popup.ts | 8 +++- src/register.ts | 7 +++ src/severity.ts | 4 ++ src/state.ts | 79 +++++++++++++++++++++++++++++++ src/templates/severity.ts | 23 +++++++-- src/{ => templates}/template.d.ts | 0 6 files changed, 115 insertions(+), 6 deletions(-) rename src/{ => templates}/template.d.ts (100%) diff --git a/src/popup.ts b/src/popup.ts index e56d7a9..e16dc1b 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -42,12 +42,18 @@ function handleSaveAsDefault(this: HTMLButtonElement, _ev: Event) { } import { severities } from "./severity.ts" +import { loadState } from "./state.ts" import { htmlSeverity } from "./templates/severity.ts" +const state = await loadState() for (const severity of severities) { const container = document.getElementById("severityControls") if (!container) throw new Error("severityControls container not found") - const newControl = htmlSeverity({ severity, count: 0, checked: true }) + const newControl = htmlSeverity({ + severity, + count: 0, + checked: state.coderabbit.visibilityState[severity], + }) container.appendChild(newControl) } diff --git a/src/register.ts b/src/register.ts index 1798760..33acfbb 100644 --- a/src/register.ts +++ b/src/register.ts @@ -1,3 +1,10 @@ +/** + * Utilities to register event listeners on HTML elements. Allows you to narrow + * down the type of the {@link HTMLElement} before getting to the handler. + * + * @module + */ + type Listener = ( this: E, ev: HTMLElementEventMap[K], diff --git a/src/severity.ts b/src/severity.ts index 2390abf..da161d2 100644 --- a/src/severity.ts +++ b/src/severity.ts @@ -7,3 +7,7 @@ export const Severity = { export type Severity = (typeof Severity)[keyof typeof Severity] export const severities: Severity[] = Object.values(Severity) + +export function isSeverity(value: unknown): value is Severity { + return Object.values(Severity).includes(value) +} diff --git a/src/state.ts b/src/state.ts index e69de29..8e65116 100644 --- a/src/state.ts +++ b/src/state.ts @@ -0,0 +1,79 @@ +import { storage } from "webextension-polyfill" +import { Severity } from "./severity.js" +import { isSeverity } from "./severity.ts" + +type CustomBotsState = Record +type VisibilityState = Record + +interface CodeRabbitState { + readonly showAllState: boolean + readonly visibilityState: VisibilityState +} + +interface State extends Record { + readonly coderabbit: CodeRabbitState + readonly customBots: CustomBotsState +} + +function isCodeRabbitState(value: unknown): value is CodeRabbitState { + if (!value || typeof value !== "object") return false + if (!("showAllState" in value) || !("visibilityState" in value)) return false + if (!isVisibilityState(value.visibilityState)) return false + if (typeof value.showAllState !== "boolean") return false + return true +} + +function isCustomBotsState(value: unknown): value is CustomBotsState { + if (!value || typeof value !== "object") return false + + return Object.entries(value).every( + ([k, v]) => typeof k === "string" && typeof v === "boolean", + ) +} + +function isState(value: unknown): value is State { + if (!value || typeof value !== "object") return false + if (!("coderabbit" in value) || !("customBots" in value)) return false + if (!isCodeRabbitState(value.coderabbit)) return false + if (!isCustomBotsState(value.customBots)) return false + return true +} + +function isVisibilityState(value: unknown): value is VisibilityState { + if (!value || typeof value !== "object") return false + + return Object.entries(value).every( + ([k, v]) => isSeverity(k) && typeof v === "boolean", + ) +} + +export async function loadState(): Promise { + const stored = await storage.sync.get(["coderabbit", "customBots"]) + if (!isState(stored)) { + console.warn("Stored state is invalid, using default state", { stored }) + return defaultState() + } + + return stored +} + +export async function saveState(state: State): Promise { + return storage.sync.set(state) +} + +function defaultState(): State { + return { + coderabbit: { + visibilityState: { + [Severity.Critical]: true, + [Severity.Major]: true, + [Severity.Minor]: true, + }, + showAllState: true, + }, + customBots: {}, + } +} + +if (!isState(defaultState())) + throw new TypeError("defaultState is not a valid State") diff --git a/src/templates/severity.ts b/src/templates/severity.ts index c91c947..71ccdeb 100644 --- a/src/templates/severity.ts +++ b/src/templates/severity.ts @@ -1,16 +1,17 @@ import type { Severity } from "../severity.ts" +import { loadState, saveState } from "../state.ts" import html from "./severity.template.html" const template = document.createElement("template") template.innerHTML = html interface Props { - readonly severity: Severity - readonly count: number readonly checked: boolean + readonly count: number + readonly severity: Severity } -export function htmlSeverity(props: Props): HTMLElement { +export function htmlSeverity(props: Props): HTMLDivElement { // 1. Clone the template; this will become our component const clone = template.content.cloneNode(true) if (!(clone instanceof DocumentFragment)) @@ -37,7 +38,7 @@ export function htmlSeverity(props: Props): HTMLElement { // 4. Extract the HTMLElement from the component and return it const firstChild = clone.firstChild - if (!(firstChild instanceof HTMLElement)) + if (!(firstChild instanceof HTMLDivElement)) throw new TypeError("firstChild is not an HTMLElement", { cause: { firstChild, clone }, }) @@ -46,6 +47,18 @@ export function htmlSeverity(props: Props): HTMLElement { function toggleSeverity(severity: Severity) { return (ev: Event) => { - console.log("Toggled severity:", severity, ev) + const checkbox = ev.target + if (!(checkbox instanceof HTMLInputElement)) + throw new TypeError( + "toggleSeverity must be registered on an HTMLInputElement", + { + cause: { ev, checkbox }, + }, + ) + + void loadState().then(state => { + state.coderabbit.visibilityState[severity] = checkbox.checked + return saveState(state) + }) } } diff --git a/src/template.d.ts b/src/templates/template.d.ts similarity index 100% rename from src/template.d.ts rename to src/templates/template.d.ts From acd77f8c065189db5e8e93459f55f993bf415760 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 21:09:32 -0500 Subject: [PATCH 10/22] =?UTF-8?q?=E2=9C=A8=20Add=20tab=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tabs.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/tabs.ts diff --git a/src/tabs.ts b/src/tabs.ts new file mode 100644 index 0000000..f47026f --- /dev/null +++ b/src/tabs.ts @@ -0,0 +1,32 @@ +import type { Tabs } from "webextension-polyfill" +import { tabs } from "webextension-polyfill" + +export async function getCurrentTab(): Promise { + const [tab] = await tabs.query({ active: true, currentWindow: true }) + return tab +} + +function isValidTab(tab: Tabs.Tab | undefined): Error | Tabs.Tab { + if (!tab) return new Error("No active tab found", { cause: { tab } }) + + if (!tab.url) return new Error("No URL", { cause: { tab } }) + + const url = new URL(tab.url) + if (url.hostname !== "github.com") + return new Error("Not a GitHub URL", { cause: { tab, url } }) + + if (!tab.id) + return new Error("No tab ID", { cause: { tab, url, id: tab.id } }) + + return tab +} + +/** Get the current tab's ID while ignoring validations. */ +export async function getTabId(): Promise { + const tab = await getCurrentTab() + + const valid = isValidTab(tab) + if (valid instanceof Error) return + + return valid.id +} From 87ca1f8e852a6c0539a7e9d25cfa6e622197bbc8 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 21:10:32 -0500 Subject: [PATCH 11/22] =?UTF-8?q?=E2=9C=A8=20Add=20session=20storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/state.ts | 70 ++++++++++++++++++++++++++++----------- src/templates/severity.ts | 4 +-- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/state.ts b/src/state.ts index 8e65116..295b6fb 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,6 +1,7 @@ import { storage } from "webextension-polyfill" import { Severity } from "./severity.js" import { isSeverity } from "./severity.ts" +import { getTabId } from "./tabs.ts" type CustomBotsState = Record type VisibilityState = Record @@ -15,6 +16,24 @@ interface State extends Record { readonly customBots: CustomBotsState } +function defaultState(): State { + return { + coderabbit: { + visibilityState: { + [Severity.Critical]: true, + [Severity.Major]: true, + [Severity.Minor]: true, + }, + showAllState: true, + }, + customBots: {}, + } +} + +function getSessionKey(tabId: number): string { + return `session_${tabId}` +} + function isCodeRabbitState(value: unknown): value is CodeRabbitState { if (!value || typeof value !== "object") return false if (!("showAllState" in value) || !("visibilityState" in value)) return false @@ -47,32 +66,45 @@ function isVisibilityState(value: unknown): value is VisibilityState { ) } +async function loadSession(): Promise { + const tabId = await getTabId() + if (!tabId) return + + const sessionKey = getSessionKey(tabId) + const session = await storage.session.get(sessionKey) + if (!isState(session)) { + console.warn("Session state is invalid", { session }) + return + } + return session +} + export async function loadState(): Promise { - const stored = await storage.sync.get(["coderabbit", "customBots"]) - if (!isState(stored)) { - console.warn("Stored state is invalid, using default state", { stored }) - return defaultState() + const session = await loadSession() + if (session) return session + + const sync = await loadSync() + if (sync) return sync + + return defaultState() +} + +async function loadSync() { + const sync = await storage.sync.get(["coderabbit", "customBots"]) + if (!isState(sync)) { + console.warn("Sync state is invalid", { sync }) + return } - return stored + return sync } -export async function saveState(state: State): Promise { - return storage.sync.set(state) +export async function saveSession(state: State): Promise { + return storage.session.set(state) } -function defaultState(): State { - return { - coderabbit: { - visibilityState: { - [Severity.Critical]: true, - [Severity.Major]: true, - [Severity.Minor]: true, - }, - showAllState: true, - }, - customBots: {}, - } +export async function saveSync(state: State): Promise { + return storage.sync.set(state) } if (!isState(defaultState())) diff --git a/src/templates/severity.ts b/src/templates/severity.ts index 71ccdeb..2364c1b 100644 --- a/src/templates/severity.ts +++ b/src/templates/severity.ts @@ -1,5 +1,5 @@ import type { Severity } from "../severity.ts" -import { loadState, saveState } from "../state.ts" +import { loadState, saveSession } from "../state.ts" import html from "./severity.template.html" const template = document.createElement("template") @@ -58,7 +58,7 @@ function toggleSeverity(severity: Severity) { void loadState().then(state => { state.coderabbit.visibilityState[severity] = checkbox.checked - return saveState(state) + return saveSession(state) }) } } From ade2a3044b1b0001703d65aed1c418bb8479c437 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Wed, 28 Jan 2026 21:34:34 -0500 Subject: [PATCH 12/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Put=20handlers=20awa?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/popup.ts | 45 +++++++++-------------------- src/popup_handlers.ts | 61 +++++++++++++++++++++++++++++++++++++++ src/state.ts | 8 ++--- src/templates/severity.ts | 1 + 4 files changed, 80 insertions(+), 35 deletions(-) create mode 100644 src/popup_handlers.ts diff --git a/src/popup.ts b/src/popup.ts index e16dc1b..33038c0 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,51 +1,33 @@ +import { + handleAddCustomBotsClick, + handleAddCustomBotsKey, + handleCodeRabbitAllToggle, + handleCustomBotAllToggle, + handleSaveAsDefault, +} from "./popup_handlers.ts" import { register } from "./register.ts" +import { severities } from "./severity.ts" +import { loadState } from "./state.ts" +import { htmlSeverity } from "./templates/severity.ts" +register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBotsClick) +register("#botNameInput", HTMLInputElement, "keypress", handleAddCustomBotsKey) register( "#coderabbitAllCheckbox", HTMLInputElement, "change", handleCodeRabbitAllToggle, ) - -function handleCodeRabbitAllToggle(this: HTMLInputElement, _ev: Event) { - console.log("checked", this.checked) -} - register( "#customBotAllCheckbox", HTMLInputElement, "change", handleCustomBotAllToggle, ) - -function handleCustomBotAllToggle(this: HTMLInputElement, _ev: Event) { - console.log("checked", this.checked) -} - -register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBots) - -function handleAddCustomBots(this: HTMLButtonElement, _ev: Event) { - console.log("Add Custom Bots clicked") -} - -register("#botNameInput", HTMLInputElement, "keypress", handleAddCustomBots2) - -function handleAddCustomBots2(this: HTMLInputElement, ev: KeyboardEvent) { - if (ev.key !== "Enter") return - console.log("Add Custom Bots clicked via Enter key") -} - register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) -function handleSaveAsDefault(this: HTMLButtonElement, _ev: Event) { - console.log("Save as Default clicked") -} - -import { severities } from "./severity.ts" -import { loadState } from "./state.ts" -import { htmlSeverity } from "./templates/severity.ts" - const state = await loadState() + for (const severity of severities) { const container = document.getElementById("severityControls") if (!container) throw new Error("severityControls container not found") @@ -55,5 +37,6 @@ for (const severity of severities) { count: 0, checked: state.coderabbit.visibilityState[severity], }) + container.appendChild(newControl) } diff --git a/src/popup_handlers.ts b/src/popup_handlers.ts new file mode 100644 index 0000000..15cb6bb --- /dev/null +++ b/src/popup_handlers.ts @@ -0,0 +1,61 @@ +import { severities, Severity } from "./severity.ts" +import { loadState, saveSession, saveSync } from "./state.ts" + +export async function handleCodeRabbitAllToggle( + this: HTMLInputElement, + _ev: Event, +): Promise { + const state = await loadState() + + state.coderabbit.showAllState = this.checked + state.coderabbit.visibilityState = { + [Severity.Critical]: this.checked, + [Severity.Major]: this.checked, + [Severity.Minor]: this.checked, + } + + await saveSession(state) + + for (const severity of severities) { + const checkbox = document.querySelector( + `#severityControls input[data-severity="${severity}"]`, + ) + + if (!checkbox) { + console.error("Checkbox not found", { severity }) + continue + } + + checkbox.checked = this.checked + } +} + +export function handleCustomBotAllToggle( + this: HTMLInputElement, + _ev: Event, +): void { + console.log("checked", this.checked) +} + +export function handleAddCustomBotsClick( + this: HTMLButtonElement, + _ev: Event, +): void { + console.log("Add Custom Bots clicked") +} + +export function handleAddCustomBotsKey( + this: HTMLInputElement, + ev: KeyboardEvent, +): void { + if (ev.key !== "Enter") return + console.log("Add Custom Bots clicked via Enter key") +} + +export async function handleSaveAsDefault( + this: HTMLButtonElement, + _ev: Event, +): Promise { + const state = await loadState() + await saveSync(state) +} diff --git a/src/state.ts b/src/state.ts index 295b6fb..eb909a7 100644 --- a/src/state.ts +++ b/src/state.ts @@ -7,13 +7,13 @@ type CustomBotsState = Record type VisibilityState = Record interface CodeRabbitState { - readonly showAllState: boolean - readonly visibilityState: VisibilityState + showAllState: boolean + visibilityState: VisibilityState } interface State extends Record { - readonly coderabbit: CodeRabbitState - readonly customBots: CustomBotsState + coderabbit: CodeRabbitState + customBots: CustomBotsState } function defaultState(): State { diff --git a/src/templates/severity.ts b/src/templates/severity.ts index 2364c1b..6121ce8 100644 --- a/src/templates/severity.ts +++ b/src/templates/severity.ts @@ -34,6 +34,7 @@ export function htmlSeverity(props: Props): HTMLDivElement { refs.name.textContent = props.severity refs.count.textContent = `(${props.count})` refs.checkbox.checked = props.checked + refs.checkbox.setAttribute("data-severity", props.severity) refs.checkbox.addEventListener("change", toggleSeverity(props.severity)) // 4. Extract the HTMLElement from the component and return it From 63b6a2d7ba75a542a4ed29f808d683504cdf6ec9 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 15:37:43 -0500 Subject: [PATCH 13/22] =?UTF-8?q?=E2=9C=A8=20Add=20`getAvailableSeverities?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/injected.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/injected.ts diff --git a/src/injected.ts b/src/injected.ts new file mode 100644 index 0000000..d9a4b85 --- /dev/null +++ b/src/injected.ts @@ -0,0 +1,46 @@ +import { Severity } from "./severity.ts" + +export function getAvailableSeverities(): Record { + const comments = Array.from( + document.querySelectorAll( + 'turbo-frame[id^="review-thread-or-comment-id-"]', + ), + ) + + const counts: Record = { + [Severity.Critical]: 0, + [Severity.Major]: 0, + [Severity.Minor]: 0, + } + + for (const comment of comments) { + const author = comment.querySelector( + 'a.author, a[data-hovercard-type="user"]', + ) + + if (author?.textContent.trim().toLowerCase().includes("coderabbit")) { + const body = comment.querySelector(".comment-body") + if (!body) continue + + const ems = Array.from(body.querySelectorAll("em")) + for (const em of ems) { + const text = em.textContent.trim() + + const severity = includesSeverity(text) + if (!severity) continue + + counts[severity] = (counts[severity] || 0) + 1 + break + } + } + } + + return counts +} + +function includesSeverity(text: string): Severity | undefined { + if (text.includes(Severity.Critical)) return Severity.Critical + if (text.includes(Severity.Major)) return Severity.Major + if (text.includes(Severity.Minor)) return Severity.Minor + return +} From 15abb2ef7f77c3864d875df68a1923c612b60983 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 15:38:07 -0500 Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=94=A7=20Remove=20`eb-ext=20lint`?= =?UTF-8?q?=20as=20it's=20buggy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2978d63..bc2adf0 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "dev": "node esbuild.ts && web-ext run --devtools --no-reload", "dev:watch": "node esbuild.ts && web-ext run --devtools", "format": "prettier --write .", - "lint": "web-ext lint --ignore-files houdini && tsgo --noEmit && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", + "lint": "tsgo --noEmit && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", "lint:fix": "eslint --fix --quiet .; markdownlint-cli2 \"**/*.md\" --fix; prettier --list-different --write .", "package": "web-ext build", "start": "node esbuild.ts && web-ext run --no-reload", From 15d845d41fcebc52f515769bf54c4fa46f264321 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 16:29:06 -0500 Subject: [PATCH 15/22] =?UTF-8?q?=E2=9C=A8=20Add=20injected=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/injected.ts | 159 +++++++++++++++++++++++++++++++++++++++++++++--- src/state.ts | 2 +- 2 files changed, 151 insertions(+), 10 deletions(-) diff --git a/src/injected.ts b/src/injected.ts index d9a4b85..bd78f3d 100644 --- a/src/injected.ts +++ b/src/injected.ts @@ -1,6 +1,21 @@ -import { Severity } from "./severity.ts" +/** + * These functions will be injected using `scripting.executeScript`. They cannot + * import outside code. + * + * @module + */ + +import type { Severity } from "./severity.ts" +import type { CustomBotsState } from "./state.ts" export function getAvailableSeverities(): Record { + function includesSeverity(text: string): Severity | undefined { + if (text.includes("Critical")) return "Critical" + if (text.includes("Major")) return "Major" + if (text.includes("Minor")) return "Minor" + return + } + const comments = Array.from( document.querySelectorAll( 'turbo-frame[id^="review-thread-or-comment-id-"]', @@ -8,9 +23,9 @@ export function getAvailableSeverities(): Record { ) const counts: Record = { - [Severity.Critical]: 0, - [Severity.Major]: 0, - [Severity.Minor]: 0, + ["Critical"]: 0, + ["Major"]: 0, + ["Minor"]: 0, } for (const comment of comments) { @@ -38,9 +53,135 @@ export function getAvailableSeverities(): Record { return counts } -function includesSeverity(text: string): Severity | undefined { - if (text.includes(Severity.Critical)) return Severity.Critical - if (text.includes(Severity.Major)) return Severity.Major - if (text.includes(Severity.Minor)) return Severity.Minor - return +export function applyVisibilityFilter( + coderabbitVisibilityState: Record, + coderabbitShowAllState: boolean, + customBots: CustomBotsState, +): void { + function getTurboFrameSeverity(turboFrame: HTMLElement) { + const inlineContainers = Array.from( + turboFrame.querySelectorAll(".js-inline-comments-container"), + ) + + for (const inlineContainer of inlineContainers) { + const authorLink = inlineContainer.querySelector( + 'a.author[href="/apps/coderabbitai"]', + ) + + if (authorLink) { + const commentBody = + inlineContainer.querySelector(".comment-body") + if (!commentBody) continue + + const ems = Array.from(commentBody.querySelectorAll("em")) + for (const em of ems) { + const text = em.textContent.trim() + if (text.includes("Critical")) return "Critical" + if (text.includes("Major")) return "Major" + if (text.includes("Minor")) return "Minor" + } + } + } + return null + } + + function isCustomBotHidden(timelineItem: HTMLDivElement) { + if (Object.keys(customBots).length === 0) return false + + const authorLinks = Array.from( + timelineItem.querySelectorAll("a.author"), + ) + + for (const authorLink of authorLinks) { + const authorName = authorLink.textContent.trim().toLowerCase() + + for (const [botName, showAll] of Object.entries(customBots)) { + if (authorName.includes(botName.toLowerCase())) { + return !showAll + } + } + } + + return false + } + + function isCodeRabbit(container: HTMLDivElement) { + return !!container.querySelector( + 'a.author[href="/apps/coderabbitai"]', + ) + } + + function updateTurboFrameVisibility( + turboFrames: HTMLElement[], + coderabbitVisibilityState: Record, + ) { + let hasVisibleTurboFrame = false + + for (const turboFrame of turboFrames) { + const severity = getTurboFrameSeverity(turboFrame) + + if (severity && coderabbitVisibilityState[severity]) { + turboFrame.style.display = "" + turboFrame.removeAttribute("data-coderabbit-hidden") + hasVisibleTurboFrame = true + } else { + turboFrame.style.display = "none" + turboFrame.setAttribute("data-coderabbit-hidden", "true") + } + } + + return hasVisibleTurboFrame + } + + const allTimelineItems = Array.from( + document.querySelectorAll(".js-timeline-item"), + ) + + for (const container of allTimelineItems) { + if (isCustomBotHidden(container)) { + container.style.display = "none" + container.setAttribute("data-custom-bot-hidden", "true") + continue + } + container.removeAttribute("data-custom-bot-hidden") + + if (!isCodeRabbit(container)) { + container.style.display = "" + continue + } + + const turboFrames = Array.from( + container.querySelectorAll( + 'turbo-frame[id^="review-thread-or-comment-id-"]', + ), + ) + + if (!coderabbitShowAllState) { + container.style.display = "" + container.removeAttribute("data-coderabbit-hidden") + + if (!updateTurboFrameVisibility(turboFrames, coderabbitVisibilityState)) { + container.style.display = "none" + container.setAttribute("data-coderabbit-hidden", "true") + } + + continue + } + + container.style.display = "" + container.removeAttribute("data-coderabbit-hidden") + + for (const turboFrame of turboFrames) { + const severity = getTurboFrameSeverity(turboFrame) + + if (severity && !coderabbitVisibilityState[severity]) { + turboFrame.style.display = "none" + turboFrame.setAttribute("data-coderabbit-hidden", "true") + continue + } + + turboFrame.style.display = "" + turboFrame.removeAttribute("data-coderabbit-hidden") + } + } } diff --git a/src/state.ts b/src/state.ts index eb909a7..25660db 100644 --- a/src/state.ts +++ b/src/state.ts @@ -3,7 +3,7 @@ import { Severity } from "./severity.js" import { isSeverity } from "./severity.ts" import { getTabId } from "./tabs.ts" -type CustomBotsState = Record +export type CustomBotsState = Record type VisibilityState = Record interface CodeRabbitState { From 5669d2bb05c245a1a3b9d7cb41a4dc7227dc2d93 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 16:49:22 -0500 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Export=20Visibili?= =?UTF-8?q?tyState?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/injected.ts | 4 ++-- src/state.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/injected.ts b/src/injected.ts index bd78f3d..53ba8e6 100644 --- a/src/injected.ts +++ b/src/injected.ts @@ -6,7 +6,7 @@ */ import type { Severity } from "./severity.ts" -import type { CustomBotsState } from "./state.ts" +import type { CustomBotsState, VisibilityState } from "./state.ts" export function getAvailableSeverities(): Record { function includesSeverity(text: string): Severity | undefined { @@ -54,7 +54,7 @@ export function getAvailableSeverities(): Record { } export function applyVisibilityFilter( - coderabbitVisibilityState: Record, + coderabbitVisibilityState: VisibilityState, coderabbitShowAllState: boolean, customBots: CustomBotsState, ): void { diff --git a/src/state.ts b/src/state.ts index 25660db..a77304e 100644 --- a/src/state.ts +++ b/src/state.ts @@ -4,7 +4,7 @@ import { isSeverity } from "./severity.ts" import { getTabId } from "./tabs.ts" export type CustomBotsState = Record -type VisibilityState = Record +export type VisibilityState = Record interface CodeRabbitState { showAllState: boolean @@ -58,7 +58,7 @@ function isState(value: unknown): value is State { return true } -function isVisibilityState(value: unknown): value is VisibilityState { +export function isVisibilityState(value: unknown): value is VisibilityState { if (!value || typeof value !== "object") return false return Object.entries(value).every( From 2141f819183ab092faa5e5d72a044bc3edfb4f74 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 17:28:06 -0500 Subject: [PATCH 17/22] =?UTF-8?q?=E2=9C=A8=20Add=20scripting=20boilerplate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/injected.ts | 33 ++++++++++---------- src/popup.ts | 45 +++------------------------ src/scripting.ts | 52 +++++++++++++++++++++++++++++++ src/setup_popup.ts | 76 ++++++++++++++++++++++++++++++++++++++++++++++ src/setup_tab.ts | 7 +++++ src/severity.ts | 25 +++++++++++---- src/state.ts | 34 ++++++++++----------- 7 files changed, 192 insertions(+), 80 deletions(-) create mode 100644 src/scripting.ts create mode 100644 src/setup_popup.ts create mode 100644 src/setup_tab.ts diff --git a/src/injected.ts b/src/injected.ts index 53ba8e6..14f0ff0 100644 --- a/src/injected.ts +++ b/src/injected.ts @@ -5,10 +5,10 @@ * @module */ -import type { Severity } from "./severity.ts" -import type { CustomBotsState, VisibilityState } from "./state.ts" +import type { Severity, SeverityCount } from "./severity.ts" +import type { State, VisibilityState } from "./state.ts" -export function getAvailableSeverities(): Record { +export function getAvailableSeverities(): SeverityCount { function includesSeverity(text: string): Severity | undefined { if (text.includes("Critical")) return "Critical" if (text.includes("Major")) return "Major" @@ -22,7 +22,7 @@ export function getAvailableSeverities(): Record { ), ) - const counts: Record = { + const counts: SeverityCount = { ["Critical"]: 0, ["Major"]: 0, ["Minor"]: 0, @@ -53,11 +53,7 @@ export function getAvailableSeverities(): Record { return counts } -export function applyVisibilityFilter( - coderabbitVisibilityState: VisibilityState, - coderabbitShowAllState: boolean, - customBots: CustomBotsState, -): void { +export function applyVisibilityFilter(state: State): void { function getTurboFrameSeverity(turboFrame: HTMLElement) { const inlineContainers = Array.from( turboFrame.querySelectorAll(".js-inline-comments-container"), @@ -86,7 +82,7 @@ export function applyVisibilityFilter( } function isCustomBotHidden(timelineItem: HTMLDivElement) { - if (Object.keys(customBots).length === 0) return false + if (Object.keys(state.customBots).length === 0) return false const authorLinks = Array.from( timelineItem.querySelectorAll("a.author"), @@ -95,7 +91,7 @@ export function applyVisibilityFilter( for (const authorLink of authorLinks) { const authorName = authorLink.textContent.trim().toLowerCase() - for (const [botName, showAll] of Object.entries(customBots)) { + for (const [botName, showAll] of Object.entries(state.customBots)) { if (authorName.includes(botName.toLowerCase())) { return !showAll } @@ -113,14 +109,14 @@ export function applyVisibilityFilter( function updateTurboFrameVisibility( turboFrames: HTMLElement[], - coderabbitVisibilityState: Record, + visibilityState: VisibilityState, ) { let hasVisibleTurboFrame = false for (const turboFrame of turboFrames) { const severity = getTurboFrameSeverity(turboFrame) - if (severity && coderabbitVisibilityState[severity]) { + if (severity && visibilityState[severity]) { turboFrame.style.display = "" turboFrame.removeAttribute("data-coderabbit-hidden") hasVisibleTurboFrame = true @@ -156,11 +152,16 @@ export function applyVisibilityFilter( ), ) - if (!coderabbitShowAllState) { + if (!state.coderabbit.showAllState) { container.style.display = "" container.removeAttribute("data-coderabbit-hidden") - if (!updateTurboFrameVisibility(turboFrames, coderabbitVisibilityState)) { + if ( + !updateTurboFrameVisibility( + turboFrames, + state.coderabbit.visibilityState, + ) + ) { container.style.display = "none" container.setAttribute("data-coderabbit-hidden", "true") } @@ -174,7 +175,7 @@ export function applyVisibilityFilter( for (const turboFrame of turboFrames) { const severity = getTurboFrameSeverity(turboFrame) - if (severity && !coderabbitVisibilityState[severity]) { + if (severity && !state.coderabbit.visibilityState[severity]) { turboFrame.style.display = "none" turboFrame.setAttribute("data-coderabbit-hidden", "true") continue diff --git a/src/popup.ts b/src/popup.ts index 33038c0..09d8208 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -1,42 +1,5 @@ -import { - handleAddCustomBotsClick, - handleAddCustomBotsKey, - handleCodeRabbitAllToggle, - handleCustomBotAllToggle, - handleSaveAsDefault, -} from "./popup_handlers.ts" -import { register } from "./register.ts" -import { severities } from "./severity.ts" -import { loadState } from "./state.ts" -import { htmlSeverity } from "./templates/severity.ts" +import { setupPopup } from "./setup_popup.ts" +import { setupTab } from "./setup_tab.ts" -register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBotsClick) -register("#botNameInput", HTMLInputElement, "keypress", handleAddCustomBotsKey) -register( - "#coderabbitAllCheckbox", - HTMLInputElement, - "change", - handleCodeRabbitAllToggle, -) -register( - "#customBotAllCheckbox", - HTMLInputElement, - "change", - handleCustomBotAllToggle, -) -register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) - -const state = await loadState() - -for (const severity of severities) { - const container = document.getElementById("severityControls") - if (!container) throw new Error("severityControls container not found") - - const newControl = htmlSeverity({ - severity, - count: 0, - checked: state.coderabbit.visibilityState[severity], - }) - - container.appendChild(newControl) -} +const popup = await setupPopup() +await setupTab(popup) diff --git a/src/scripting.ts b/src/scripting.ts new file mode 100644 index 0000000..d46d163 --- /dev/null +++ b/src/scripting.ts @@ -0,0 +1,52 @@ +/** + * Bridge between the popup and injected scripts on the GitHub page. + * + * @module + */ + +import { scripting } from "webextension-polyfill" +import { applyVisibilityFilter, getAvailableSeverities } from "./injected.js" +import { isSeverityCount, type SeverityCount } from "./severity.ts" +import type { State } from "./state.ts" + +/** Apply visibility filters to comments on the GitHub page. */ +export async function applyFilters(tabId: number, state: State): Promise { + await scripting + .executeScript({ + target: { tabId }, + func: applyVisibilityFilter, + args: [state], + }) + .catch((error: unknown) => { + console.error("Error applying filters", { error }) + }) +} + +/** Scan the GitHub page for CodeRabbit comments by severity level. */ +export async function scanSeverities( + tabId: number, +): Promise { + const results = await scripting + .executeScript({ + target: { tabId }, + func: getAvailableSeverities, + }) + .catch((error: unknown) => { + console.error("Error scanning severities", { error }) + return + }) + + const { result } = results?.[0] ?? {} + + if (!result) { + console.error("No scan results", { results }) + return + } + + if (!isSeverityCount(result)) { + console.error("Invalid scan results", { results }) + return + } + + return result +} diff --git a/src/setup_popup.ts b/src/setup_popup.ts new file mode 100644 index 0000000..fd324e3 --- /dev/null +++ b/src/setup_popup.ts @@ -0,0 +1,76 @@ +import { + handleAddCustomBotsClick, + handleAddCustomBotsKey, + handleCodeRabbitAllToggle, + handleCustomBotAllToggle, + handleSaveAsDefault, +} from "./popup_handlers.ts" +import { register } from "./register.ts" +import { scanSeverities } from "./scripting.ts" +import { newSeverityCount, severities, type SeverityCount } from "./severity.ts" +import type { State } from "./state.ts" +import { loadState } from "./state.ts" +import { getTabId } from "./tabs.ts" +import { htmlSeverity } from "./templates/severity.ts" + +export interface PopupContext { + readonly state: State + readonly tabId: number | undefined +} + +async function getSeveritiesFromTab( + tabId: number | undefined, +): Promise { + if (!tabId) return newSeverityCount() + + const result = await scanSeverities(tabId) + if (!result) return newSeverityCount() + + return result +} + +export async function setupPopup(): Promise { + register("#addBotBtn", HTMLButtonElement, "click", handleAddCustomBotsClick) + + register( + "#botNameInput", + HTMLInputElement, + "keypress", + handleAddCustomBotsKey, + ) + + register( + "#coderabbitAllCheckbox", + HTMLInputElement, + "change", + handleCodeRabbitAllToggle, + ) + + register( + "#customBotAllCheckbox", + HTMLInputElement, + "change", + handleCustomBotAllToggle, + ) + + register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) + + const state = await loadState() + const tabId = await getTabId() + const foundSeverities = await getSeveritiesFromTab(tabId) + + for (const severity of severities) { + const container = document.getElementById("severityControls") + if (!container) throw new Error("severityControls container not found") + + const newControl = htmlSeverity({ + checked: state.coderabbit.visibilityState[severity], + count: foundSeverities[severity], + severity, + }) + + container.appendChild(newControl) + } + + return { state, tabId } +} diff --git a/src/setup_tab.ts b/src/setup_tab.ts new file mode 100644 index 0000000..afbf3ef --- /dev/null +++ b/src/setup_tab.ts @@ -0,0 +1,7 @@ +import { applyFilters } from "./scripting.ts" +import type { PopupContext } from "./setup_popup.ts" + +export async function setupTab(popup: PopupContext): Promise { + if (!popup.tabId) return + await applyFilters(popup.tabId, popup.state) +} diff --git a/src/severity.ts b/src/severity.ts index da161d2..3d27764 100644 --- a/src/severity.ts +++ b/src/severity.ts @@ -1,13 +1,26 @@ +export type Severity = (typeof Severity)[keyof typeof Severity] +export type SeverityCount = Record + +export function isSeverity(value: unknown): value is Severity { + return Object.values(Severity).includes(value) +} + +export function isSeverityCount(value: unknown): value is SeverityCount { + if (!value || typeof value !== "object") return false + + return Object.entries(value).every( + ([k, v]) => isSeverity(k) && typeof v === "number", + ) +} + +export function newSeverityCount(): SeverityCount { + return { [Severity.Critical]: 0, [Severity.Major]: 0, [Severity.Minor]: 0 } +} + export const Severity = { Critical: "Critical", Major: "Major", Minor: "Minor", } as const -export type Severity = (typeof Severity)[keyof typeof Severity] - export const severities: Severity[] = Object.values(Severity) - -export function isSeverity(value: unknown): value is Severity { - return Object.values(Severity).includes(value) -} diff --git a/src/state.ts b/src/state.ts index a77304e..194117f 100644 --- a/src/state.ts +++ b/src/state.ts @@ -11,25 +11,11 @@ interface CodeRabbitState { visibilityState: VisibilityState } -interface State extends Record { +export interface State extends Record { coderabbit: CodeRabbitState customBots: CustomBotsState } -function defaultState(): State { - return { - coderabbit: { - visibilityState: { - [Severity.Critical]: true, - [Severity.Major]: true, - [Severity.Minor]: true, - }, - showAllState: true, - }, - customBots: {}, - } -} - function getSessionKey(tabId: number): string { return `session_${tabId}` } @@ -86,7 +72,7 @@ export async function loadState(): Promise { const sync = await loadSync() if (sync) return sync - return defaultState() + return newState() } async function loadSync() { @@ -99,6 +85,20 @@ async function loadSync() { return sync } +function newState(): State { + return { + coderabbit: { + visibilityState: { + [Severity.Critical]: true, + [Severity.Major]: true, + [Severity.Minor]: true, + }, + showAllState: true, + }, + customBots: {}, + } +} + export async function saveSession(state: State): Promise { return storage.session.set(state) } @@ -107,5 +107,5 @@ export async function saveSync(state: State): Promise { return storage.sync.set(state) } -if (!isState(defaultState())) +if (!isState(newState())) throw new TypeError("defaultState is not a valid State") From 66b6c5f2ca19ab228b1d72fe7fa0efe40b7c02e7 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 18:48:30 -0500 Subject: [PATCH 18/22] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Prop=20drill=20tabId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/popup_handlers.ts | 45 +++++++++++++++++++-------------------- src/setup_popup.ts | 3 ++- src/state.ts | 13 +++++------ src/templates/severity.ts | 14 +++++++----- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index bc2adf0..e1d4b44 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "format": "prettier --write .", "lint": "tsgo --noEmit && eslint . && markdownlint-cli2 \"**/*.md\" && prettier --check .", "lint:fix": "eslint --fix --quiet .; markdownlint-cli2 \"**/*.md\" --fix; prettier --list-different --write .", - "package": "web-ext build", + "package": "node esbuild.ts && web-ext build --overwrite-dest", "start": "node esbuild.ts && web-ext run --no-reload", "web-ext": "web-ext" }, diff --git a/src/popup_handlers.ts b/src/popup_handlers.ts index 15cb6bb..2742d96 100644 --- a/src/popup_handlers.ts +++ b/src/popup_handlers.ts @@ -1,11 +1,25 @@ +import { applyFilters } from "./scripting.ts" import { severities, Severity } from "./severity.ts" import { loadState, saveSession, saveSync } from "./state.ts" +import { getTabId } from "./tabs.ts" + +export function handleAddCustomBotsClick(this: HTMLButtonElement): void { + console.log("Add Custom Bots clicked") +} + +export function handleAddCustomBotsKey( + this: HTMLInputElement, + ev: KeyboardEvent, +): void { + if (ev.key !== "Enter") return + console.log("Add Custom Bots clicked via Enter key") +} export async function handleCodeRabbitAllToggle( this: HTMLInputElement, - _ev: Event, ): Promise { - const state = await loadState() + const tabId = await getTabId() + const state = await loadState(tabId) state.coderabbit.showAllState = this.checked state.coderabbit.visibilityState = { @@ -28,34 +42,19 @@ export async function handleCodeRabbitAllToggle( checkbox.checked = this.checked } -} -export function handleCustomBotAllToggle( - this: HTMLInputElement, - _ev: Event, -): void { - console.log("checked", this.checked) + if (tabId) await applyFilters(tabId, state) } -export function handleAddCustomBotsClick( - this: HTMLButtonElement, - _ev: Event, -): void { - console.log("Add Custom Bots clicked") -} - -export function handleAddCustomBotsKey( - this: HTMLInputElement, - ev: KeyboardEvent, -): void { - if (ev.key !== "Enter") return - console.log("Add Custom Bots clicked via Enter key") +export function handleCustomBotAllToggle(this: HTMLInputElement): void { + console.log("checked", this.checked) } export async function handleSaveAsDefault( this: HTMLButtonElement, - _ev: Event, ): Promise { - const state = await loadState() + const tabId = await getTabId() + const state = await loadState(tabId) await saveSync(state) + if (tabId) await applyFilters(tabId, state) } diff --git a/src/setup_popup.ts b/src/setup_popup.ts index fd324e3..0583f33 100644 --- a/src/setup_popup.ts +++ b/src/setup_popup.ts @@ -55,8 +55,8 @@ export async function setupPopup(): Promise { register("#saveAsDefaultBtn", HTMLButtonElement, "click", handleSaveAsDefault) - const state = await loadState() const tabId = await getTabId() + const state = await loadState(tabId) const foundSeverities = await getSeveritiesFromTab(tabId) for (const severity of severities) { @@ -67,6 +67,7 @@ export async function setupPopup(): Promise { checked: state.coderabbit.visibilityState[severity], count: foundSeverities[severity], severity, + tabId, }) container.appendChild(newControl) diff --git a/src/state.ts b/src/state.ts index 194117f..01549d8 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,7 +1,6 @@ import { storage } from "webextension-polyfill" import { Severity } from "./severity.js" import { isSeverity } from "./severity.ts" -import { getTabId } from "./tabs.ts" export type CustomBotsState = Record export type VisibilityState = Record @@ -52,9 +51,10 @@ export function isVisibilityState(value: unknown): value is VisibilityState { ) } -async function loadSession(): Promise { - const tabId = await getTabId() - if (!tabId) return +async function loadSession( + tabId: number | undefined, +): Promise { + if (!tabId) return undefined const sessionKey = getSessionKey(tabId) const session = await storage.session.get(sessionKey) @@ -62,11 +62,12 @@ async function loadSession(): Promise { console.warn("Session state is invalid", { session }) return } + return session } -export async function loadState(): Promise { - const session = await loadSession() +export async function loadState(tabId: number | undefined): Promise { + const session = await loadSession(tabId) if (session) return session const sync = await loadSync() diff --git a/src/templates/severity.ts b/src/templates/severity.ts index 6121ce8..4ad7bcb 100644 --- a/src/templates/severity.ts +++ b/src/templates/severity.ts @@ -1,3 +1,4 @@ +import { applyFilters } from "../scripting.ts" import type { Severity } from "../severity.ts" import { loadState, saveSession } from "../state.ts" import html from "./severity.template.html" @@ -9,6 +10,7 @@ interface Props { readonly checked: boolean readonly count: number readonly severity: Severity + readonly tabId: number | undefined } export function htmlSeverity(props: Props): HTMLDivElement { @@ -35,7 +37,7 @@ export function htmlSeverity(props: Props): HTMLDivElement { refs.count.textContent = `(${props.count})` refs.checkbox.checked = props.checked refs.checkbox.setAttribute("data-severity", props.severity) - refs.checkbox.addEventListener("change", toggleSeverity(props.severity)) + refs.checkbox.addEventListener("change", toggleSeverity(props)) // 4. Extract the HTMLElement from the component and return it const firstChild = clone.firstChild @@ -46,7 +48,7 @@ export function htmlSeverity(props: Props): HTMLDivElement { return firstChild } -function toggleSeverity(severity: Severity) { +function toggleSeverity(props: Props) { return (ev: Event) => { const checkbox = ev.target if (!(checkbox instanceof HTMLInputElement)) @@ -57,9 +59,11 @@ function toggleSeverity(severity: Severity) { }, ) - void loadState().then(state => { - state.coderabbit.visibilityState[severity] = checkbox.checked - return saveSession(state) + void loadState(props.tabId).then(async state => { + state.coderabbit.visibilityState[props.severity] = checkbox.checked + await saveSession(state) + + if (props.tabId) await applyFilters(props.tabId, state) }) } } From 43c85bb5888f9589befb4dacb2991c2c6aefe11d Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Fri, 30 Jan 2026 18:56:50 -0500 Subject: [PATCH 19/22] =?UTF-8?q?=E2=9C=85=20Add=20unit=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 ++ src/severity.test.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/severity.test.ts diff --git a/package.json b/package.json index e1d4b44..2734643 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,8 @@ "lint:fix": "eslint --fix --quiet .; markdownlint-cli2 \"**/*.md\" --fix; prettier --list-different --write .", "package": "node esbuild.ts && web-ext build --overwrite-dest", "start": "node esbuild.ts && web-ext run --no-reload", + "test": "vitest run", + "test:watch": "vitest", "web-ext": "web-ext" }, "devDependencies": { diff --git a/src/severity.test.ts b/src/severity.test.ts new file mode 100644 index 0000000..db67be2 --- /dev/null +++ b/src/severity.test.ts @@ -0,0 +1,32 @@ +import { describe, test } from "vitest" +import { isSeverity, isSeverityCount, Severity } from "./severity.ts" + +describe("isSeverity", () => { + test("true", ({ expect }) => { + const severity = isSeverity("Critical") + expect(severity).toBe(true) + }) + + test("false", ({ expect }) => { + const severity = isSeverity("Unknown") + expect(severity).toBe(false) + }) +}) + +describe("isSeverityCount", () => { + test("true", ({ expect }) => { + const count = isSeverityCount({ + [Severity.Critical]: 0, + [Severity.Major]: 0, + [Severity.Minor]: 0, + }) + expect(count).toBe(true) + }) + + test("false", ({ expect }) => { + const count = isSeverityCount({ + Unknown: 0, + }) + expect(count).toBe(false) + }) +}) From 02cd07a97c923cc87577b93b7ba45236683c4042 Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Sat, 31 Jan 2026 00:34:31 -0500 Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=92=9A=20No=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/node.js.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/node.js.yaml b/.github/workflows/node.js.yaml index 3e478b5..9246e22 100644 --- a/.github/workflows/node.js.yaml +++ b/.github/workflows/node.js.yaml @@ -33,7 +33,6 @@ jobs: - run: pnpm run build --noEmit - run: pnpm run lint - run: pnpm run test - - run: pnpm run test:coverage - if: github.actor != 'nektos/act' uses: actions/upload-artifact@v6 From 8bc3be478ea74f656f62e61022c030512fe9723a Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Sat, 31 Jan 2026 00:42:04 -0500 Subject: [PATCH 21/22] =?UTF-8?q?=F0=9F=92=9A=20Less=20coverage=20stuff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/node.js.yaml | 8 -------- package.json | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/node.js.yaml b/.github/workflows/node.js.yaml index 9246e22..132d8a1 100644 --- a/.github/workflows/node.js.yaml +++ b/.github/workflows/node.js.yaml @@ -34,14 +34,6 @@ jobs: - run: pnpm run lint - run: pnpm run test - - if: github.actor != 'nektos/act' - uses: actions/upload-artifact@v6 - with: - name: coverage - path: coverage - compression-level: 9 - if-no-files-found: error - fix: permissions: contents: write diff --git a/package.json b/package.json index 2734643..4ba4b6e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "package": "node esbuild.ts && web-ext build --overwrite-dest", "start": "node esbuild.ts && web-ext run --no-reload", "test": "vitest run", + "test:coverage": "vitest run --coverage", "test:watch": "vitest", "web-ext": "web-ext" }, From 868f48e72144272f9a273325cb7c72f1975fc09b Mon Sep 17 00:00:00 2001 From: Nathan Gendron Date: Sat, 31 Jan 2026 00:42:14 -0500 Subject: [PATCH 22/22] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20import=20{=20buil?= =?UTF-8?q?d=20}=20from=20"esbuild"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esbuild.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esbuild.ts b/esbuild.ts index 6ddf3e9..b91db6b 100644 --- a/esbuild.ts +++ b/esbuild.ts @@ -1,6 +1,6 @@ -import * as esbuild from "esbuild" +import { build } from "esbuild" -await esbuild.build({ +await build({ banner: { js: `// Copyright 2026 CodeRabbit