From ba76e74a5516189c0d240bf7587898ca6b8f725c Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 15:44:43 +0100 Subject: [PATCH 1/6] Add comprehensive test coverage and refactor extension into modular structure. --- .github/workflows/test.yml | 5 +- .vscode-test.mjs | 11 + .vscode/launch.json | 5 +- .vscodeignore | 2 + CONTRIBUTING.md | 22 +- package-lock.json | 2321 ++++++++++++++++++- package.json | 9 +- src/extension.ts | 615 +---- src/generators/docblockGenerator.ts | 42 + src/generators/snippetGenerator.ts | 83 + src/providers/callbackCompletionProvider.ts | 327 +++ src/providers/hookCompletionProvider.ts | 29 + src/providers/hoverProvider.ts | 29 + src/test/suite/callbackCompletion.test.ts | 108 + src/test/suite/edgeCases.test.ts | 190 ++ src/test/suite/hookCompletion.test.ts | 103 + src/test/suite/hoverProvider.test.ts | 105 + src/types/index.ts | 15 + src/utils/hookHelpers.ts | 86 + src/utils/matchers.ts | 18 + src/utils/symbolHelpers.ts | 35 + src/utils/typeHelpers.ts | 93 + 22 files changed, 3587 insertions(+), 666 deletions(-) create mode 100644 .vscode-test.mjs create mode 100644 src/generators/docblockGenerator.ts create mode 100644 src/generators/snippetGenerator.ts create mode 100644 src/providers/callbackCompletionProvider.ts create mode 100644 src/providers/hookCompletionProvider.ts create mode 100644 src/providers/hoverProvider.ts create mode 100644 src/test/suite/callbackCompletion.test.ts create mode 100644 src/test/suite/edgeCases.test.ts create mode 100644 src/test/suite/hookCompletion.test.ts create mode 100644 src/test/suite/hoverProvider.test.ts create mode 100644 src/types/index.ts create mode 100644 src/utils/hookHelpers.ts create mode 100644 src/utils/matchers.ts create mode 100644 src/utils/symbolHelpers.ts create mode 100644 src/utils/typeHelpers.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 198e272..f0bf3e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ on: jobs: test: if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - name: Linter + name: Test runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -30,3 +30,6 @@ jobs: - name: Run the build run: npm run build + + - name: Run tests + run: xvfb-run -a npm test diff --git a/.vscode-test.mjs b/.vscode-test.mjs new file mode 100644 index 0000000..cd9593a --- /dev/null +++ b/.vscode-test.mjs @@ -0,0 +1,11 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out/test/suite/**/*.test.js', + version: 'stable', + launchArgs: ['--disable-extensions'], + mocha: { + ui: 'tdd', + timeout: 20000 + } +}); diff --git a/.vscode/launch.json b/.vscode/launch.json index 336301a..14ee2bc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,10 +8,11 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}/.vscode/sample.php" ], "outFiles": [ - "${workspaceFolder}/dist/**/*.js" + "${workspaceFolder}/out/**/*.js" ], "preLaunchTask": "npm: build" } diff --git a/.vscodeignore b/.vscodeignore index ec481e6..5bbde46 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -6,5 +6,7 @@ **/*.ts images/screenshot* src/** +out/test/** test/** +.vscode-test.mjs tsconfig.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c43fcea..c7be49e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,26 @@ You can clone this repo and then run the extension in development mode in VS Cod 2. Open the directory in VS Code and launch the extension from the `Run` panel. -The extension source code can be found in `src/extension.ts`. +The extension source code is in the `src/` directory with the following structure: +- `src/extension.ts` - Entry point +- `src/providers/` - Completion and hover providers +- `src/utils/` - Helper functions for hooks, types, and matchers +- `src/generators/` - Code generation for docblocks and snippets +- `src/types/` - TypeScript interfaces + +### Running Tests + +Run the test suite: + +```bash +npm test +``` + +This runs all tests in a headless VS Code instance. Tests are located in `src/test/suite/` and cover: +- Hook completion (actions and filters) +- Callback completion (closures, arrow functions, utility functions) +- Hover provider functionality +- Edge cases and various hook functions ## Releasing a New Version @@ -31,6 +50,7 @@ These are the steps to take to release a new version of the extension (for contr ### Prior to Release 1. Check [the milestone on GitHub](https://github.com/wp-hooks/vscode-wordpress-hooks/milestones) for open issues or PRs. Fix or reassign as necessary. +1. Run `npm test` to ensure all tests pass. 1. Ensure `readme.md` contains an up to date description, FAQs, screenshots, etc. 1. Ensure `.vscodeignore` is up to date with all files that shouldn't be part of the build. 1. Prepare a changelog for [the Releases page on GitHub](https://github.com/wp-hooks/vscode-wordpress-hooks/releases). diff --git a/package-lock.json b/package-lock.json index 9fc3ead..102e20f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,19 @@ "@wp-hooks/wordpress-core": "^1.10.0" }, "devDependencies": { + "@types/glob": "^8.1.0", + "@types/mocha": "^10.0.10", "@types/node": "^20", "@types/vscode": "^1", "@typescript-eslint/eslint-plugin": "^5", "@typescript-eslint/parser": "^5", + "@vscode/test-cli": "^0.0.11", + "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3", "eslint": "^8", "eslint-config-airbnb-base": "^15", "eslint-plugin-import": "^2", + "mocha": "^11.7.3", "typescript": "^5" }, "engines": { @@ -254,6 +259,13 @@ "node": ">=16" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -390,6 +402,44 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -425,6 +475,35 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -437,6 +516,20 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", @@ -647,6 +740,198 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vscode/test-cli": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.11.tgz", + "integrity": "sha512-qO332yvzFqGhBMJrp6TdwbIydiHgCtxXc2Nl6M58mbH/Z+0CyLR76Jzv4YWPEthhrARprzCRJUqzFvTHFhTj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^11.1.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-cli/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@vscode/test-cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@vscode/test-cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vscode/test-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@vscode/vsce": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.1.tgz", @@ -1099,6 +1384,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1228,6 +1527,19 @@ ], "optional": true }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1268,6 +1580,13 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1309,6 +1628,32 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1331,6 +1676,19 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1385,6 +1743,22 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -1392,51 +1766,135 @@ "dev": true, "optional": true }, - "node_modules/cockatiel": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", - "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, "engines": { - "node": ">=16" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "delayed-stream": "~1.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, "engines": { "node": ">= 6" @@ -1454,6 +1912,20 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1497,12 +1969,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1513,6 +1986,19 @@ } } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -1591,6 +2077,16 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1704,6 +2200,20 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -1804,6 +2314,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2254,6 +2774,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -2327,6 +2857,21 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2360,6 +2905,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -2491,6 +3059,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", @@ -2578,6 +3153,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -2590,6 +3175,13 @@ "node": ">=10" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -2667,6 +3259,13 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2755,6 +3354,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2857,6 +3469,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -2902,6 +3527,16 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -2979,6 +3614,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3004,12 +3652,58 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", @@ -3124,6 +3818,52 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -3181,6 +3921,16 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3251,8 +4001,25 @@ "dev": true, "license": "MIT" }, - "node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, @@ -3263,6 +4030,22 @@ "node": ">=10" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3320,6 +4103,19 @@ "node": ">= 0.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -3371,11 +4167,151 @@ "dev": true, "optional": true }, + "node_modules/mocha": { + "version": "11.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.3.tgz", + "integrity": "sha512-iorDKDzBKgVk/npVkW2S+b57ekA9+xKWijVvNpgPMl1odxeB4HavgiydLN54Lhyn/jpcM+Z/BohCzIvHmfaPCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/mocha/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -3422,6 +4358,16 @@ "dev": true, "optional": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -3510,6 +4456,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -3545,6 +4507,140 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3582,6 +4678,13 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3712,6 +4815,13 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3760,6 +4870,13 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -3825,6 +4942,16 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -3878,6 +5005,20 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -3895,6 +5036,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -3921,6 +5072,23 @@ "node": ">=4" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4022,6 +5190,23 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4123,7 +5308,20 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/stoppable": { @@ -4333,6 +5531,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -4363,6 +5575,21 @@ "node": ">=6" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4555,8 +5782,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "optional": true + "dev": true }, "node_modules/uuid": { "version": "8.3.2", @@ -4568,6 +5794,21 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4628,6 +5869,13 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -4744,12 +5992,89 @@ "node": ">=4.0" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -4974,6 +6299,12 @@ "uuid": "^8.3.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -5066,6 +6397,34 @@ } } }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5092,6 +6451,29 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dev": true, + "requires": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -5104,6 +6486,18 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true + }, "@types/node": { "version": "20.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", @@ -5224,6 +6618,136 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@vscode/test-cli": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.11.tgz", + "integrity": "sha512-qO332yvzFqGhBMJrp6TdwbIydiHgCtxXc2Nl6M58mbH/Z+0CyLR76Jzv4YWPEthhrARprzCRJUqzFvTHFhTj7Q==", + "dev": true, + "requires": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^11.1.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true + } + } + }, + "@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "requires": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + } + }, "@vscode/vsce": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.1.tgz", @@ -5535,6 +7059,16 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5619,6 +7153,12 @@ "dev": true, "optional": true }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -5656,6 +7196,12 @@ "fill-range": "^7.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5679,6 +7225,25 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "dev": true }, + "c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5695,6 +7260,12 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5734,6 +7305,15 @@ "domutils": "^3.0.1" } }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "requires": { + "readdirp": "^4.0.1" + } + }, "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -5741,6 +7321,62 @@ "dev": true, "optional": true }, + "cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "requires": { + "restore-cursor": "^5.0.0" + } + }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "cockatiel": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", @@ -5789,6 +7425,18 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5820,14 +7468,20 @@ "dev": true }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, "decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -5880,6 +7534,12 @@ "dev": true, "optional": true }, + "diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5966,6 +7626,16 @@ "once": "^1.4.0" } }, + "enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, "entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -6045,6 +7715,12 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -6402,6 +8078,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -6461,6 +8143,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -6485,6 +8174,18 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true + }, "get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -6577,6 +8278,12 @@ "get-intrinsic": "^1.1.3" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", @@ -6634,6 +8341,12 @@ "has-symbols": "^1.0.2" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -6643,6 +8356,12 @@ "lru-cache": "^6.0.0" } }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -6688,6 +8407,12 @@ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -6758,6 +8483,15 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -6819,6 +8553,12 @@ "is-extglob": "^2.1.1" } }, + "is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -6846,6 +8586,12 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -6896,6 +8642,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -6914,12 +8666,45 @@ "is-docker": "^2.0.0" } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, "jackspeak": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", @@ -7012,6 +8797,50 @@ } } }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -7060,6 +8889,15 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -7117,6 +8955,16 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "dev": true }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7126,6 +8974,15 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7163,6 +9020,12 @@ "mime-db": "1.52.0" } }, + "mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true + }, "mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -7198,10 +9061,107 @@ "dev": true, "optional": true }, + "mocha": { + "version": "11.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.3.tgz", + "integrity": "sha512-iorDKDzBKgVk/npVkW2S+b57ekA9+xKWijVvNpgPMl1odxeB4HavgiydLN54Lhyn/jpcM+Z/BohCzIvHmfaPCw==", + "dev": true, + "requires": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "mute-stream": { @@ -7246,6 +9206,12 @@ "dev": true, "optional": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -7310,6 +9276,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "requires": { + "mimic-function": "^5.0.0" + } + }, "open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -7335,6 +9310,87 @@ "word-wrap": "^1.2.3" } }, + "ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "requires": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true + }, + "emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true + }, + "is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true + }, + "log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "requires": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "dependencies": { + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + } + } + }, + "string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "requires": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7359,6 +9415,12 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7458,6 +9520,12 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -7491,6 +9559,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7529,6 +9603,15 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -7572,6 +9655,12 @@ "util-deprecate": "^1.0.1" } }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -7583,6 +9672,12 @@ "functions-have-names": "^1.2.2" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -7600,6 +9695,16 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "requires": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + } + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7653,6 +9758,21 @@ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7710,6 +9830,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true + }, "stoppable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", @@ -7851,6 +9977,12 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "dev": true + }, "tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -7878,6 +10010,17 @@ "readable-stream": "^3.1.1" } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8028,8 +10171,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "optional": true + "dev": true }, "uuid": { "version": "8.3.2", @@ -8037,6 +10179,17 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, + "v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8079,6 +10232,12 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true + }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -8155,12 +10314,70 @@ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index 9b758bc..49a5445 100644 --- a/package.json +++ b/package.json @@ -71,17 +71,24 @@ "deploy-vsce": "vsce publish", "vscode:prepublish": "npm run build", "watch": "tsc --watch", - "build": "tsc" + "build": "tsc", + "pretest": "npm run build", + "test": "vscode-test" }, "devDependencies": { + "@types/glob": "^8.1.0", + "@types/mocha": "^10.0.10", "@types/node": "^20", "@types/vscode": "^1", "@typescript-eslint/eslint-plugin": "^5", "@typescript-eslint/parser": "^5", + "@vscode/test-cli": "^0.0.11", + "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3", "eslint": "^8", "eslint-config-airbnb-base": "^15", "eslint-plugin-import": "^2", + "mocha": "^11.7.3", "typescript": "^5" }, "dependencies": { diff --git a/src/extension.ts b/src/extension.ts index c9a1547..b376ad0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,617 +1,14 @@ import * as vscode from 'vscode'; -import { hooks as actions } from '@wp-hooks/wordpress-core/hooks/actions.json'; -import { hooks as filters } from '@wp-hooks/wordpress-core/hooks/filters.json'; -import { Hook, Tag } from '@wp-hooks/wordpress-core/interface'; - -const extensionName = 'vscode-wordpress-hooks'; - -function getHookCompletion( - hook: Hook, -): vscode.CompletionItem { - const completion = new vscode.CompletionItem(hook.name, vscode.CompletionItemKind.Value); - completion.detail = hook.doc.description; - completion.documentation = getHookDescription(hook); - - if (hook.aliases) { - completion.filterText = hook.aliases.join(' '); - } - - return completion; -} - -function getHookDescription( - hook: Hook, -): vscode.MarkdownString { - let description = hook.doc.long_description; - const slug = getHookSlug(hook); - - description += `\n\n[View on developer.wordpress.org →](https://developer.wordpress.org/reference/hooks/${slug}/)\n\n`; - - const params = hook.doc.tags.filter((tag) => tag.name === 'param'); - - params.forEach((tag: Tag) => { - if (!tag.types) { - return; - } - - const types = tag.types.join('|'); - description += `\n\n_@param_ \`${types} ${tag.variable}\` \n${tag.content}`; - }); - - const everythingElse = hook.doc.tags.filter((tag) => tag.name !== 'param'); - - everythingElse.forEach((tag: Tag) => { - description += `\n\n_@${tag.name}_ ${tag.content || tag.refers || ''} ${tag.description || ''}`; - }); - - return new vscode.MarkdownString(description); -} - -function getHookSlug( - hook: Hook, -): string { - return hook.name.toLowerCase().replace(/[^a-z_-]/g, ''); -} - -function isInFilter( - line: string, -): RegExpMatchArray | null { - return line.match(/(add|remove|has|doing)_filter\([\s]*('|")[^"|']*$/); -} - -function isInAction( - line: string, -): RegExpMatchArray | null { - return line.match(/(add|remove|has|doing|did)_action\([\s]*('|")[^"|']*$/); -} - -function isInFunctionDeclaration( - line: string, -): RegExpMatchArray | null { - // add_ filter|action ( '" {hook} '" , - return line.match(/add_(?:filter|action)\(\s*['"](?\S+?)['"],\s*\w*?$/); -} - -function getHook( - name: string, -): Hook | void { - let hooks = filters.filter((filter) => { - if (filter.name === name) { - return true; - } - - if (filter.aliases?.includes(name)) { - return true; - } - - return false; - }); - - if (hooks.length === 0) { - hooks = actions.filter((action) => { - if (action.name === name) { - return true; - } - - if (action.aliases?.includes(name)) { - return true; - } - - return false; - }); - } - - if (hooks.length) { - return hooks[0]; - } -} - -interface tagType { - type: string; - nullable: boolean; -} - -function getTagType( - tag: Tag, -): tagType | null { - // https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration - const allowedTypes: { [key: string]: number } = { - self: 5.0, - array: 5.1, - callable: 5.4, - bool: 7.0, - float: 7.0, - int: 7.0, - string: 7.0, - iterable: 7.1, - object: 7.2, - }; - - const typeData: tagType = { - type: '', - nullable: false, - }; - - // No type info? Bail. - if (!tag.types) { - return null; - } - - const types = [...tag.types]; - - // Handle nullable type. - if (types.length === 2) { - if (types[0] === 'null') { - types.splice(0, 1); - typeData.nullable = true; - } else if (types[1] === 'null') { - types.splice(1, 1); - typeData.nullable = true; - } - } - - // More than one type? Bail. - if (types.length !== 1) { - return null; - } - - let type = types[0]; - - // Un-hintable type? Bail. - if (['mixed'].includes(type)) { - return null; - } - - // Hinting for typed-arrays. - if (type.indexOf('[]') !== -1) { - type = 'array'; - } - - // Aliases for bool. - if (['false', 'true', 'boolean'].includes(type)) { - type = 'bool'; - } - - // Alias for callable. - if (type === 'callback') { - type = 'callable'; - } - - // Alias for int. - if (type === 'integer') { - type = 'int'; - } - - // Convert stdClass to object to avoid fatals when the stdClass gets promoted to a real class. - if (type === '\\stdClass') { - type = 'object'; - } - - // Check the allowed types, ignoring unknown types such as class and interface names. - if (allowedTypes[type]) { - return null; - } - - typeData.type = type; - - return typeData; -} - -function getReturnType( - tag: Tag, -) : tagType | null { - return getTagType(tag); -} - -interface contextualPosition { - symbol: vscode.DocumentSymbol | null; - inNamespace: boolean; - inMethod: boolean; - inFunction: boolean; -} - -function getContainingSymbol( - symbols: vscode.DocumentSymbol[], - position: vscode.Position, -) : contextualPosition { - const inside = symbols.filter((symbol) => symbol.range.contains(position)); - const inNamespace = symbols.filter((symbol) => (vscode.SymbolKind.Namespace === symbol.kind)).length > 0; - - const context: contextualPosition = { - symbol: null, - inNamespace, - inMethod: false, - inFunction: false, - }; - - if (!inside.length) { - return context; - } - - [context.symbol] = inside; - - if (context.symbol.children.length) { - const methods = context.symbol.children.filter((symbol) => symbol.range.contains(position)); - if (methods.length) { - [context.symbol] = methods; - } - } - - context.inMethod = (context.symbol.kind === vscode.SymbolKind.Method); - context.inFunction = (context.symbol.kind === vscode.SymbolKind.Function); - - return context; -} +import { createHookCompletionProvider } from './providers/hookCompletionProvider'; +import { createCallbackCompletionProvider } from './providers/callbackCompletionProvider'; +import { createHoverProvider } from './providers/hoverProvider'; export function activate( context: vscode.ExtensionContext, ): void { - const hooksProvider = vscode.languages.registerCompletionItemProvider( - 'php', - { - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - // get all text until the `position` and check if it reads a certain value and if so then complete - const linePrefix = document.lineAt(position).text.substr(0, position.character); - - if (isInAction(linePrefix)) { - return actions.map(getHookCompletion); - } - - if (isInFilter(linePrefix)) { - return filters.map(getHookCompletion); - } - - return undefined; - }, - }, - "'", - '"', - ); - - const callbackProvider = vscode.languages.registerCompletionItemProvider( - 'php', - { - provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - // get all text until the `position` and check if it reads a certain value and if so then complete - const linePrefix = document.lineAt(position).text.substr(0, position.character); - const declaration = isInFunctionDeclaration(linePrefix); - - if (!declaration) { - return undefined; - } - - const hook = getHook(declaration.groups?.hook || ''); - - if (!hook) { - return undefined; - } - - const completions: vscode.CompletionItem[] = []; - - const params = hook.doc.tags.filter((tag) => tag.name === 'param'); - const snippetArgsString = params.map((param) => { - const val = `\\${param.variable}`; - const type = getTagType(param); - - if (!type) { - return val; - } - - if (!type.nullable) { - return `${type.type} ${val}`; - } - - return `?${type.type} ${val}`; - }).join(', '); - const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); - - let snippetCallback = ''; - let documentationCallback = ''; - const docblockLines = [ - '/**', - ` * ${hook.doc.description}`, - ' *', - ]; - const paramTypeLengths: number[] = [0]; - const paramNameLengths: number[] = [0]; - - params.forEach((param) => { - if (param.types) { - paramTypeLengths.push(param.types.join('|').length); - } - if (param.variable) { - paramNameLengths.push(param.variable.length); - } - }); - - const longestParamType = Math.max(...paramTypeLengths); - const longestParamName = Math.max(...paramNameLengths); - - params.forEach((param) => { - const types = param.types?.join('|').padEnd(longestParamType, ' ') || ''; - const variable = param.variable?.padEnd(longestParamName, ' ') || ''; - docblockLines.push(` * @param ${types} ${variable} ${param.content}`); - }); - - const suffix = (params.length > 1 ? `, 10, ${params.length} ` : ' '); - let returnTypeString = ''; - - if (hook.type === 'filter' || hook.type === 'filter_reference') { - const returnType = getReturnType(params[0]); - - if (returnType) { - if (returnType.nullable) { - returnTypeString = ` : ?${returnType.type}`; - } else { - returnTypeString = ` : ${returnType.type}`; - } - } - - snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; - documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; - - docblockLines.push(` * @return ${params[0].types?.join('|') || ''} ${params[0].content}`); - } else { - const actionArgsString = snippetArgsString ? ` ${snippetArgsString} ` : ''; - snippetCallback = `(${actionArgsString}) : void {\n\t\${1}\n}`; - documentationCallback = `(${docArgsString}) : void {\n}`; - } - - docblockLines.push(' */'); - - const docBlocksEnabled: boolean = vscode.workspace.getConfiguration(extensionName).get('docBlocks.enable') ?? true; - const lineLeadingMatch = document.lineAt(position).text.match(/^[\s]+/); - const lineLeadingWhitespace = lineLeadingMatch ? lineLeadingMatch[0] : ''; - - const completionItemForClosure = new vscode.CompletionItem('Closure', vscode.CompletionItemKind.Function); - completionItemForClosure.insertText = new vscode.SnippetString(`function${snippetCallback}${suffix}`); - completionItemForClosure.documentation = `function${documentationCallback}${suffix}`; - completionItemForClosure.preselect = true; - completionItemForClosure.sortText = '1'; - - if (docBlocksEnabled) { - completionItemForClosure.additionalTextEdits = [ - vscode.TextEdit.insert(position.with({ character: 0 }), `${docblockLines.map((line) => `${lineLeadingWhitespace}${line}`).join('\n')}\n`), - ]; - } - - completions.push(completionItemForClosure); - - if (hook.type === 'filter') { - const completionItemForArrow = new vscode.CompletionItem('Arrow function', vscode.CompletionItemKind.Function); - - const snippetArrow = `( ${snippetArgsString} )${returnTypeString} => \\${params[0].variable}\${1}`; - const documentationArrow = `( ${docArgsString} )${returnTypeString} => ${params[0].variable}`; - - completionItemForArrow.insertText = new vscode.SnippetString(`fn${snippetArrow}${suffix}`); - completionItemForArrow.documentation = `fn${documentationArrow}${suffix}`; - completionItemForArrow.sortText = '2'; - - if (docBlocksEnabled) { - completionItemForArrow.additionalTextEdits = [ - vscode.TextEdit.insert(position.with({ character: 0 }), `${docblockLines.map((line) => `${lineLeadingWhitespace}${line}`).join('\n')}\n`), - ]; - } - - completions.push(completionItemForArrow); - - const snippets = { - __return_true: 'Return true', - __return_false: 'Return false', - __return_zero: 'Return zero', - __return_empty_array: 'Return empty array', - __return_empty_string: 'Return empty string', - }; - const snippetTypes: { [key: string]: string[] } = { - null: [ - ], - self: [ - ], - array: [ - '__return_empty_array', - ], - callable: [ - ], - bool: [ - '__return_true', - '__return_false', - ], - float: [ - '__return_zero', - ], - int: [ - '__return_zero', - ], - string: [ - '__return_empty_string', - ], - iterable: [ - '__return_empty_array', - ], - object: [ - ], - }; - - for (const [snippet, documentation] of Object.entries(snippets)) { - // If we don't know the types, show this snippet: - let show = !params[0].types; - - if (params[0].types) { - for (const paramType of params[0].types) { - // If there's a parameter type which we're not aware of, show this snippet: - if (!(paramType in snippetTypes)) { - show = true; - break; - } - - // If this parameter type supports this snippet, show it: - if (snippetTypes[paramType].includes(snippet)) { - show = true; - } - } - } - - if (show) { - const itemSnippet = `'${snippet}' `; - const completionItemForReturn = new vscode.CompletionItem(documentation, vscode.CompletionItemKind.Function); - - completionItemForReturn.insertText = new vscode.SnippetString(itemSnippet); - completionItemForReturn.documentation = itemSnippet; - completionItemForReturn.sortText = '3'; - - completions.push(completionItemForReturn); - } - } - - const snippet = '\'__return_null\' '; - - const completionItemForReturnNull = new vscode.CompletionItem('Return null', vscode.CompletionItemKind.Function); - completionItemForReturnNull.insertText = new vscode.SnippetString(snippet); - completionItemForReturnNull.documentation = snippet; - completionItemForReturnNull.sortText = '4'; - - completions.push(completionItemForReturnNull); - } - - if (vscode.window.activeTextEditor !== undefined) { - return vscode.commands - .executeCommand( - 'vscode.executeDocumentSymbolProvider', - vscode.window.activeTextEditor.document.uri, - ) - .then((symbols) => { - const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; - const completionItemForFunction = new vscode.CompletionItem('Function', vscode.CompletionItemKind.Function); - const insertFunction = `function ${functionName}${documentationCallback}`; - let insertionPosition = document.lineAt(position.line).range.end; - - completionItemForFunction.insertText = new vscode.SnippetString(`'${functionName}'${suffix}`); - completionItemForFunction.documentation = `'${functionName}'${suffix}\n\nfunction ${functionName}${documentationCallback}`; - - completionItemForFunction.preselect = true; - completionItemForFunction.sortText = '0'; - completionItemForFunction.additionalTextEdits = []; - - if (symbols === undefined) { - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, '\n\n'), - ); - - if (docBlocksEnabled) { - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, `${docblockLines.join('\n')}\n`), - ); - } - - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, insertFunction), - ); - - completions.push(completionItemForFunction); - - return completions; - } - - const positionContext = getContainingSymbol(symbols, position); - - let leadingMatch = null; - - if (positionContext.symbol) { - leadingMatch = document.lineAt(positionContext.symbol.range.end).text.match(/^[\s]+/); - } else { - leadingMatch = document.lineAt(position).text.match(/^[\s]+/); - } - - const leadingWhitespace = leadingMatch ? leadingMatch[0] : ''; - - if (positionContext.inMethod && positionContext.symbol) { - const completionItemForMethod = new vscode.CompletionItem('Class method', vscode.CompletionItemKind.Method); - completionItemForMethod.insertText = new vscode.SnippetString(`[ \\$this, '${functionName}' ]${suffix}`); - completionItemForMethod.documentation = `[ $this, '${functionName}' ]${suffix}\n\npublic function ${functionName}${documentationCallback}`; - completionItemForMethod.preselect = true; - completionItemForMethod.sortText = '0'; - completionItemForMethod.additionalTextEdits = []; - - let insertMethod = `public function ${functionName}${documentationCallback}`; - - insertMethod = insertMethod.split('\n').map((line) => `${leadingWhitespace}${line}`).join('\n'); - - completionItemForMethod.additionalTextEdits.push( - vscode.TextEdit.insert(positionContext.symbol.range.end, '\n\n'), - ); - - if (docBlocksEnabled) { - completionItemForMethod.additionalTextEdits.push( - vscode.TextEdit.insert(positionContext.symbol.range.end, `${docblockLines.map((line) => `${leadingWhitespace}${line}`).join('\n')}\n`), - ); - } - - completionItemForMethod.additionalTextEdits.push( - vscode.TextEdit.insert(positionContext.symbol.range.end, insertMethod), - ); - - completions.push(completionItemForMethod); - } else { - if (positionContext.inNamespace) { - completionItemForFunction.insertText = new vscode.SnippetString(`__NAMESPACE__ . '\\\\\\\\${functionName}'${suffix}`); - completionItemForFunction.documentation = `__NAMESPACE__ . '\\\\${functionName}'${suffix}\n\nfunction ${functionName}${documentationCallback}`; - } - - if (positionContext.symbol) { - insertionPosition = positionContext.symbol.range.end; - } - - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, '\n\n'), - ); - - if (docBlocksEnabled) { - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, `${docblockLines.map((line) => `${leadingWhitespace}${line}`).join('\n')}\n`), - ); - } - - completionItemForFunction.additionalTextEdits.push( - vscode.TextEdit.insert(insertionPosition, insertFunction), - ); - - completions.push(completionItemForFunction); - } - - return completions; - }); - } - - return completions; - }, - }, - ',', - ' ', - ); - - const hoverProvider = vscode.languages.registerHoverProvider( - 'php', - { - provideHover(document, position) { - const linePrefix = document.lineAt(position).text.substr(0, position.character); - - if (!isInAction(linePrefix) && !isInFilter(linePrefix)) { - return undefined; - } - - const hook = getHook(document.getText(document.getWordRangeAtPosition(position))); - - if (!hook) { - return undefined; - } - - return new vscode.Hover([ - new vscode.MarkdownString().appendCodeblock(hook.doc.description), - getHookDescription(hook), - ]); - }, - }, - ); + const hooksProvider = createHookCompletionProvider(); + const callbackProvider = createCallbackCompletionProvider(); + const hoverProvider = createHoverProvider(); context.subscriptions.push(hooksProvider, callbackProvider, hoverProvider); } diff --git a/src/generators/docblockGenerator.ts b/src/generators/docblockGenerator.ts new file mode 100644 index 0000000..c1c145a --- /dev/null +++ b/src/generators/docblockGenerator.ts @@ -0,0 +1,42 @@ +import { Tag } from '../types'; + +export function generateDocblockLines( + description: string, + params: Tag[], + returnParam?: Tag +): string[] { + const docblockLines = [ + '/**', + ` * ${description}`, + ' *', + ]; + + const paramTypeLengths: number[] = [0]; + const paramNameLengths: number[] = [0]; + + params.forEach((param) => { + if (param.types) { + paramTypeLengths.push(param.types.join('|').length); + } + if (param.variable) { + paramNameLengths.push(param.variable.length); + } + }); + + const longestParamType = Math.max(...paramTypeLengths); + const longestParamName = Math.max(...paramNameLengths); + + params.forEach((param) => { + const types = param.types?.join('|').padEnd(longestParamType, ' ') || ''; + const variable = param.variable?.padEnd(longestParamName, ' ') || ''; + docblockLines.push(` * @param ${types} ${variable} ${param.content}`); + }); + + if (returnParam) { + docblockLines.push(` * @return ${returnParam.types?.join('|') || ''} ${returnParam.content}`); + } + + docblockLines.push(' */'); + + return docblockLines; +} diff --git a/src/generators/snippetGenerator.ts b/src/generators/snippetGenerator.ts new file mode 100644 index 0000000..664fd66 --- /dev/null +++ b/src/generators/snippetGenerator.ts @@ -0,0 +1,83 @@ +import { Tag } from '../types'; +import { getTagType, getReturnType } from '../utils/typeHelpers'; + +export interface CallbackSnippet { + snippetCallback: string; + documentationCallback: string; + returnTypeString: string; + suffix: string; +} + +export function generateCallbackSnippet( + hookType: string, + params: Tag[] +): CallbackSnippet { + const snippetArgsString = params.map((param) => { + const val = `\\${param.variable}`; + const type = getTagType(param); + + if (!type) { + return val; + } + + if (!type.nullable) { + return `${type.type} ${val}`; + } + + return `?${type.type} ${val}`; + }).join(', '); + + const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); + const suffix = (params.length > 1 ? `, 10, ${params.length} ` : ' '); + + let snippetCallback = ''; + let documentationCallback = ''; + let returnTypeString = ''; + + if (hookType === 'filter' || hookType === 'filter_reference') { + const returnType = getReturnType(params[0]); + + if (returnType) { + if (returnType.nullable) { + returnTypeString = ` : ?${returnType.type}`; + } else { + returnTypeString = ` : ${returnType.type}`; + } + } + + snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; + documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; + } else { + const actionArgsString = snippetArgsString ? ` ${snippetArgsString} ` : ''; + snippetCallback = `(${actionArgsString}) : void {\n\t\${1}\n}`; + documentationCallback = `(${docArgsString}) : void {\n}`; + } + + return { + snippetCallback, + documentationCallback, + returnTypeString, + suffix + }; +} + +export const WORDPRESS_UTILITY_SNIPPETS = { + __return_true: 'Return true', + __return_false: 'Return false', + __return_zero: 'Return zero', + __return_empty_array: 'Return empty array', + __return_empty_string: 'Return empty string', +}; + +export const SNIPPET_TYPES: { [key: string]: string[] } = { + null: [], + self: [], + array: ['__return_empty_array'], + callable: [], + bool: ['__return_true', '__return_false'], + float: ['__return_zero'], + int: ['__return_zero'], + string: ['__return_empty_string'], + iterable: ['__return_empty_array'], + object: [], +}; diff --git a/src/providers/callbackCompletionProvider.ts b/src/providers/callbackCompletionProvider.ts new file mode 100644 index 0000000..8779afc --- /dev/null +++ b/src/providers/callbackCompletionProvider.ts @@ -0,0 +1,327 @@ +import * as vscode from 'vscode'; +import { Hook } from '../types'; +import { getHook } from '../utils/hookHelpers'; +import { isInFunctionDeclaration } from '../utils/matchers'; +import { getContainingSymbol } from '../utils/symbolHelpers'; +import { generateDocblockLines } from '../generators/docblockGenerator'; +import { + generateCallbackSnippet, + WORDPRESS_UTILITY_SNIPPETS, + SNIPPET_TYPES +} from '../generators/snippetGenerator'; + +const extensionName = 'vscode-wordpress-hooks'; + +function createClosureCompletion( + hook: Hook, + docblockLines: string[], + snippetCallback: string, + documentationCallback: string, + suffix: string, + lineLeadingWhitespace: string, + position: vscode.Position, + docBlocksEnabled: boolean +): vscode.CompletionItem { + const completionItemForClosure = new vscode.CompletionItem('Closure', vscode.CompletionItemKind.Function); + completionItemForClosure.insertText = new vscode.SnippetString(`function${snippetCallback}${suffix}`); + completionItemForClosure.documentation = `function${documentationCallback}${suffix}`; + completionItemForClosure.preselect = true; + completionItemForClosure.sortText = '1'; + + if (docBlocksEnabled) { + completionItemForClosure.additionalTextEdits = [ + vscode.TextEdit.insert(position.with({ character: 0 }), `${docblockLines.map((line) => `${lineLeadingWhitespace}${line}`).join('\n')}\n`), + ]; + } + + return completionItemForClosure; +} + +function createArrowFunctionCompletion( + params: Array<{ variable?: string }>, + returnTypeString: string, + snippetArgsString: string, + docArgsString: string, + suffix: string, + docblockLines: string[], + lineLeadingWhitespace: string, + position: vscode.Position, + docBlocksEnabled: boolean +): vscode.CompletionItem { + const completionItemForArrow = new vscode.CompletionItem('Arrow function', vscode.CompletionItemKind.Function); + + const snippetArrow = `( ${snippetArgsString} )${returnTypeString} => \\${params[0].variable}\${1}`; + const documentationArrow = `( ${docArgsString} )${returnTypeString} => ${params[0].variable}`; + + completionItemForArrow.insertText = new vscode.SnippetString(`fn${snippetArrow}${suffix}`); + completionItemForArrow.documentation = `fn${documentationArrow}${suffix}`; + completionItemForArrow.sortText = '2'; + + if (docBlocksEnabled) { + completionItemForArrow.additionalTextEdits = [ + vscode.TextEdit.insert(position.with({ character: 0 }), `${docblockLines.map((line) => `${lineLeadingWhitespace}${line}`).join('\n')}\n`), + ]; + } + + return completionItemForArrow; +} + +function createUtilityFunctionCompletions( + params: Array<{ types?: string[] }> +): vscode.CompletionItem[] { + const completions: vscode.CompletionItem[] = []; + + for (const [snippet, documentation] of Object.entries(WORDPRESS_UTILITY_SNIPPETS)) { + // If we don't know the types, show this snippet: + let show = !params[0].types; + + if (params[0].types) { + for (const paramType of params[0].types) { + // If there's a parameter type which we're not aware of, show this snippet: + if (!(paramType in SNIPPET_TYPES)) { + show = true; + break; + } + + // If this parameter type supports this snippet, show it: + if (SNIPPET_TYPES[paramType].includes(snippet)) { + show = true; + } + } + } + + if (show) { + const itemSnippet = `'${snippet}' `; + const completionItemForReturn = new vscode.CompletionItem(documentation, vscode.CompletionItemKind.Function); + + completionItemForReturn.insertText = new vscode.SnippetString(itemSnippet); + completionItemForReturn.documentation = itemSnippet; + completionItemForReturn.sortText = '3'; + + completions.push(completionItemForReturn); + } + } + + // Always add __return_null + const snippet = '\'__return_null\' '; + const completionItemForReturnNull = new vscode.CompletionItem('Return null', vscode.CompletionItemKind.Function); + completionItemForReturnNull.insertText = new vscode.SnippetString(snippet); + completionItemForReturnNull.documentation = snippet; + completionItemForReturnNull.sortText = '4'; + completions.push(completionItemForReturnNull); + + return completions; +} + +function createFunctionCompletion( + hook: Hook, + documentationCallback: string, + suffix: string, + docblockLines: string[], + leadingWhitespace: string, + insertionPosition: vscode.Position, + docBlocksEnabled: boolean +): vscode.CompletionItem { + const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; + const completionItemForFunction = new vscode.CompletionItem('Function', vscode.CompletionItemKind.Function); + const insertFunction = `function ${functionName}${documentationCallback}`; + + completionItemForFunction.insertText = new vscode.SnippetString(`'${functionName}'${suffix}`); + completionItemForFunction.documentation = `'${functionName}'${suffix}\n\nfunction ${functionName}${documentationCallback}`; + completionItemForFunction.preselect = true; + completionItemForFunction.sortText = '0'; + completionItemForFunction.additionalTextEdits = []; + + completionItemForFunction.additionalTextEdits.push( + vscode.TextEdit.insert(insertionPosition, '\n\n'), + ); + + if (docBlocksEnabled) { + completionItemForFunction.additionalTextEdits.push( + vscode.TextEdit.insert(insertionPosition, `${docblockLines.join('\n')}\n`), + ); + } + + completionItemForFunction.additionalTextEdits.push( + vscode.TextEdit.insert(insertionPosition, insertFunction), + ); + + return completionItemForFunction; +} + +function createMethodCompletion( + hook: Hook, + documentationCallback: string, + suffix: string, + docblockLines: string[], + leadingWhitespace: string, + methodInsertionPosition: vscode.Position, + docBlocksEnabled: boolean +): vscode.CompletionItem { + const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; + const completionItemForMethod = new vscode.CompletionItem('Class method', vscode.CompletionItemKind.Method); + + completionItemForMethod.insertText = new vscode.SnippetString(`[ \\$this, '${functionName}' ]${suffix}`); + completionItemForMethod.documentation = `[ $this, '${functionName}' ]${suffix}\n\npublic function ${functionName}${documentationCallback}`; + completionItemForMethod.preselect = true; + completionItemForMethod.sortText = '0'; + completionItemForMethod.additionalTextEdits = []; + + let insertMethod = `public function ${functionName}${documentationCallback}`; + insertMethod = insertMethod.split('\n').map((line) => `${leadingWhitespace}${line}`).join('\n'); + + completionItemForMethod.additionalTextEdits.push( + vscode.TextEdit.insert(methodInsertionPosition, '\n\n'), + ); + + if (docBlocksEnabled) { + completionItemForMethod.additionalTextEdits.push( + vscode.TextEdit.insert(methodInsertionPosition, `${docblockLines.map((line) => `${leadingWhitespace}${line}`).join('\n')}\n`), + ); + } + + completionItemForMethod.additionalTextEdits.push( + vscode.TextEdit.insert(methodInsertionPosition, insertMethod), + ); + + return completionItemForMethod; +} + +export function createCallbackCompletionProvider(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider( + 'php', + { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { + const linePrefix = document.lineAt(position).text.substr(0, position.character); + const declaration = isInFunctionDeclaration(linePrefix); + + if (!declaration) { + return undefined; + } + + const hook = getHook(declaration.groups?.hook || ''); + + if (!hook) { + return undefined; + } + + const completions: vscode.CompletionItem[] = []; + const params = hook.doc.tags.filter((tag) => tag.name === 'param'); + + // Generate callback snippet + const { snippetCallback, documentationCallback, returnTypeString, suffix } = generateCallbackSnippet(hook.type, params); + const snippetArgsString = params.map((param) => `\\${param.variable}`).join(', '); + const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); + + // Generate docblock + const returnParam = (hook.type === 'filter' || hook.type === 'filter_reference') ? params[0] : undefined; + const docblockLines = generateDocblockLines(hook.doc.description, params, returnParam); + + const docBlocksEnabled: boolean = vscode.workspace.getConfiguration(extensionName).get('docBlocks.enable') ?? true; + const lineLeadingMatch = document.lineAt(position).text.match(/^[\s]+/); + const lineLeadingWhitespace = lineLeadingMatch ? lineLeadingMatch[0] : ''; + + // Add closure completion + completions.push(createClosureCompletion( + hook, + docblockLines, + snippetCallback, + documentationCallback, + suffix, + lineLeadingWhitespace, + position, + docBlocksEnabled + )); + + // Add arrow function and utility functions for filters + if (hook.type === 'filter') { + completions.push(createArrowFunctionCompletion( + params, + returnTypeString, + snippetArgsString, + docArgsString, + suffix, + docblockLines, + lineLeadingWhitespace, + position, + docBlocksEnabled + )); + + completions.push(...createUtilityFunctionCompletions(params)); + } + + // Add function/method completions + if (vscode.window.activeTextEditor !== undefined) { + return vscode.commands + .executeCommand( + 'vscode.executeDocumentSymbolProvider', + vscode.window.activeTextEditor.document.uri, + ) + .then((symbols) => { + let insertionPosition = document.lineAt(position.line).range.end; + + if (symbols === undefined) { + completions.push(createFunctionCompletion( + hook, + documentationCallback, + suffix, + docblockLines, + '', + insertionPosition, + docBlocksEnabled + )); + return completions; + } + + const positionContext = getContainingSymbol(symbols, position); + + let leadingMatch = null; + if (positionContext.symbol) { + leadingMatch = document.lineAt(positionContext.symbol.range.end).text.match(/^[\s]+/); + } else { + leadingMatch = document.lineAt(position).text.match(/^[\s]+/); + } + + const leadingWhitespace = leadingMatch ? leadingMatch[0] : ''; + + if (positionContext.inMethod && positionContext.symbol) { + completions.push(createMethodCompletion( + hook, + documentationCallback, + suffix, + docblockLines, + leadingWhitespace, + positionContext.symbol.range.end, + docBlocksEnabled + )); + } else { + const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; + const completionItemForFunction = createFunctionCompletion( + hook, + documentationCallback, + suffix, + docblockLines, + leadingWhitespace, + positionContext.symbol ? positionContext.symbol.range.end : insertionPosition, + docBlocksEnabled + ); + + if (positionContext.inNamespace) { + completionItemForFunction.insertText = new vscode.SnippetString(`__NAMESPACE__ . '\\\\\\\\${functionName}'${suffix}`); + completionItemForFunction.documentation = `__NAMESPACE__ . '\\\\${functionName}'${suffix}\n\nfunction ${functionName}${documentationCallback}`; + } + + completions.push(completionItemForFunction); + } + + return completions; + }); + } + + return completions; + }, + }, + ',', + ' ', + ); +} diff --git a/src/providers/hookCompletionProvider.ts b/src/providers/hookCompletionProvider.ts new file mode 100644 index 0000000..5d0b410 --- /dev/null +++ b/src/providers/hookCompletionProvider.ts @@ -0,0 +1,29 @@ +import * as vscode from 'vscode'; +import { hooks as actions } from '@wp-hooks/wordpress-core/hooks/actions.json'; +import { hooks as filters } from '@wp-hooks/wordpress-core/hooks/filters.json'; +import { getHookCompletion } from '../utils/hookHelpers'; +import { isInAction, isInFilter } from '../utils/matchers'; + +export function createHookCompletionProvider(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider( + 'php', + { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { + // get all text until the `position` and check if it reads a certain value and if so then complete + const linePrefix = document.lineAt(position).text.substr(0, position.character); + + if (isInAction(linePrefix)) { + return actions.map(getHookCompletion); + } + + if (isInFilter(linePrefix)) { + return filters.map(getHookCompletion); + } + + return undefined; + }, + }, + "'", + '"', + ); +} diff --git a/src/providers/hoverProvider.ts b/src/providers/hoverProvider.ts new file mode 100644 index 0000000..3db219c --- /dev/null +++ b/src/providers/hoverProvider.ts @@ -0,0 +1,29 @@ +import * as vscode from 'vscode'; +import { getHook, getHookDescription } from '../utils/hookHelpers'; +import { isInAction, isInFilter } from '../utils/matchers'; + +export function createHoverProvider(): vscode.Disposable { + return vscode.languages.registerHoverProvider( + 'php', + { + provideHover(document, position) { + const linePrefix = document.lineAt(position).text.substr(0, position.character); + + if (!isInAction(linePrefix) && !isInFilter(linePrefix)) { + return undefined; + } + + const hook = getHook(document.getText(document.getWordRangeAtPosition(position))); + + if (!hook) { + return undefined; + } + + return new vscode.Hover([ + new vscode.MarkdownString().appendCodeblock(hook.doc.description), + getHookDescription(hook), + ]); + }, + }, + ); +} diff --git a/src/test/suite/callbackCompletion.test.ts b/src/test/suite/callbackCompletion.test.ts new file mode 100644 index 0000000..8a39863 --- /dev/null +++ b/src/test/suite/callbackCompletion.test.ts @@ -0,0 +1,108 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('Callback Completion Test Suite', () => { + test('Should provide callback completions for add_action', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 20); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + // Should have standard callback types + assert.ok( + labels.some(label => label === 'Closure' || label.includes('Closure')), + 'Should include Closure option' + ); + assert.ok( + labels.some(label => label === 'Function' || label.includes('Function')), + 'Should include Function option' + ); + }); + + test('Should provide callback completions for add_filter', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 32); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + // Should have callback types including arrow function for filters + assert.ok( + labels.some(label => label === 'Arrow function' || label.includes('Arrow')), + 'Should include Arrow function option for filters' + ); + }); + + test('Should include WordPress utility functions for filters', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 32); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + // Check for WordPress utility functions + const hasUtilityFunctions = labels.some(label => + label && ( + label.includes('Return true') || + label.includes('Return false') || + label.includes('Return null') || + label.includes('return') + ) + ); + + assert.ok( + hasUtilityFunctions, + `Should include WordPress utility function options. Got: ${labels.slice(0, 15).join(', ')}` + ); + }); +}); diff --git a/src/test/suite/edgeCases.test.ts b/src/test/suite/edgeCases.test.ts new file mode 100644 index 0000000..746b651 --- /dev/null +++ b/src/test/suite/edgeCases.test.ts @@ -0,0 +1,190 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('Edge Cases Test Suite', () => { + test('Should handle remove_action', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 16); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + assert.ok(labels.includes('init'), 'Should include actions for remove_action'); + }); + + test('Should handle remove_filter', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 16); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + assert.ok(labels.includes('the_content'), 'Should include filters for remove_filter'); + }); + + test('Should handle has_action', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 13); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + }); + + test('Should handle has_filter', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 13); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + }); + + test('Should handle doing_action', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 16); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + }); + + test('Should handle did_action', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 14); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + }); + + test('Should handle double quotes', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: 'add_action("' + }); + + await vscode.window.showTextDocument(doc); + await new Promise(resolve => setTimeout(resolve, 100)); + + const position = new vscode.Position(0, 12); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Should work with double quotes'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + assert.ok(labels.includes('init'), 'Should include actions with double quotes'); + }); + + test('Should handle extra whitespace', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 16); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Should handle extra whitespace'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + }); +}); diff --git a/src/test/suite/hookCompletion.test.ts b/src/test/suite/hookCompletion.test.ts new file mode 100644 index 0000000..3f0c55b --- /dev/null +++ b/src/test/suite/hookCompletion.test.ts @@ -0,0 +1,103 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('Hook Completion Test Suite', () => { + vscode.window.showInformationMessage('Start hook completion tests.'); + + test('Should provide autocomplete for add_action', async () => { + // Create a PHP document with content that triggers our completion provider + const content = " setTimeout(resolve, 100)); + + // Position is right after the opening quote + const position = new vscode.Position(1, 12); + + // Execute the completion provider + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + // Check that we have WordPress actions + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + + // Should have "init" action + assert.ok( + labels.includes('init'), + `Should include "init" action. Got ${labels.length} items, first 10: ${labels.slice(0, 10).join(', ')}` + ); + }); + + test('Should provide autocomplete for add_filter', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: "( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + // Check for a common WordPress filter + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + assert.ok( + labels.some(label => label === 'the_content'), + 'Should include "the_content" filter' + ); + }); + + test('Should not provide hook completions outside of hook functions', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: "( + 'vscode.executeCompletionItemProvider', + doc.uri, + position + ); + + // Either no completions or the completions shouldn't be WordPress hooks + if (completions && completions.items.length > 0) { + const labels = completions.items.map(item => + typeof item.label === 'string' ? item.label : item.label.label + ); + // Our extension shouldn't provide completions here + // Other extensions might, so we just check our hooks aren't there + assert.ok( + !labels.some(label => label === 'init' || label === 'wp_enqueue_scripts'), + 'Should not provide WordPress action completions in random strings' + ); + } + }); +}); diff --git a/src/test/suite/hoverProvider.test.ts b/src/test/suite/hoverProvider.test.ts new file mode 100644 index 0000000..f8dc98f --- /dev/null +++ b/src/test/suite/hoverProvider.test.ts @@ -0,0 +1,105 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; + +suite('Hover Provider Test Suite', () => { + test('Should provide hover information for actions', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + // Position over 'init' + const position = new vscode.Position(1, 14); + + const hovers = await vscode.commands.executeCommand( + 'vscode.executeHoverProvider', + doc.uri, + position + ); + + assert.ok(hovers, 'Hover should be returned'); + assert.ok(hovers.length > 0, 'Should have hover information'); + + const hoverText = hovers.map(h => + h.contents.map(c => + typeof c === 'string' ? c : c.value + ).join('') + ).join(''); + + assert.ok( + hoverText.toLowerCase().includes('wordpress') || + hoverText.toLowerCase().includes('fires') || + hoverText.toLowerCase().includes('loaded'), + 'Hover should contain WordPress hook description' + ); + }); + + test('Should provide hover information for filters', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + // Position over 'the_content' + const position = new vscode.Position(1, 18); + + const hovers = await vscode.commands.executeCommand( + 'vscode.executeHoverProvider', + doc.uri, + position + ); + + assert.ok(hovers, 'Hover should be returned'); + assert.ok(hovers.length > 0, 'Should have hover information'); + + const hoverText = hovers.map(h => + h.contents.map(c => + typeof c === 'string' ? c : c.value + ).join('') + ).join(''); + + assert.ok( + hoverText.toLowerCase().includes('filter') || + hoverText.toLowerCase().includes('content'), + 'Hover should contain filter description' + ); + }); + + test('Should include link to developer.wordpress.org', async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: " setTimeout(resolve, 100)); + + const position = new vscode.Position(1, 16); + + const hovers = await vscode.commands.executeCommand( + 'vscode.executeHoverProvider', + doc.uri, + position + ); + + assert.ok(hovers, 'Hover should be returned'); + assert.ok(hovers.length > 0, 'Should have hover information'); + + const hoverText = hovers.map(h => + h.contents.map(c => + typeof c === 'string' ? c : c.value + ).join('') + ).join(''); + + assert.ok( + hoverText.includes('developer.wordpress.org'), + 'Hover should include link to WordPress developer docs' + ); + }); +}); diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..1e6efff --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,15 @@ +import * as vscode from 'vscode'; + +export { Hook, Tag } from '@wp-hooks/wordpress-core/interface'; + +export interface TagType { + type: string; + nullable: boolean; +} + +export interface ContextualPosition { + symbol: vscode.DocumentSymbol | null; + inNamespace: boolean; + inMethod: boolean; + inFunction: boolean; +} diff --git a/src/utils/hookHelpers.ts b/src/utils/hookHelpers.ts new file mode 100644 index 0000000..25936bf --- /dev/null +++ b/src/utils/hookHelpers.ts @@ -0,0 +1,86 @@ +import * as vscode from 'vscode'; +import { hooks as actions } from '@wp-hooks/wordpress-core/hooks/actions.json'; +import { hooks as filters } from '@wp-hooks/wordpress-core/hooks/filters.json'; +import { Hook, Tag } from '../types'; + +export function getHook( + name: string, +): Hook | void { + let hooks = filters.filter((filter) => { + if (filter.name === name) { + return true; + } + + if (filter.aliases?.includes(name)) { + return true; + } + + return false; + }); + + if (hooks.length === 0) { + hooks = actions.filter((action) => { + if (action.name === name) { + return true; + } + + if (action.aliases?.includes(name)) { + return true; + } + + return false; + }); + } + + if (hooks.length) { + return hooks[0]; + } +} + +export function getHookSlug( + hook: Hook, +): string { + return hook.name.toLowerCase().replace(/[^a-z_-]/g, ''); +} + +export function getHookDescription( + hook: Hook, +): vscode.MarkdownString { + let description = hook.doc.long_description; + const slug = getHookSlug(hook); + + description += `\n\n[View on developer.wordpress.org →](https://developer.wordpress.org/reference/hooks/${slug}/)\n\n`; + + const params = hook.doc.tags.filter((tag) => tag.name === 'param'); + + params.forEach((tag: Tag) => { + if (!tag.types) { + return; + } + + const types = tag.types.join('|'); + description += `\n\n_@param_ \`${types} ${tag.variable}\` \n${tag.content}`; + }); + + const everythingElse = hook.doc.tags.filter((tag) => tag.name !== 'param'); + + everythingElse.forEach((tag: Tag) => { + description += `\n\n_@${tag.name}_ ${tag.content || tag.refers || ''} ${tag.description || ''}`; + }); + + return new vscode.MarkdownString(description); +} + +export function getHookCompletion( + hook: Hook, +): vscode.CompletionItem { + const completion = new vscode.CompletionItem(hook.name, vscode.CompletionItemKind.Value); + completion.detail = hook.doc.description; + completion.documentation = getHookDescription(hook); + + if (hook.aliases) { + completion.filterText = hook.aliases.join(' '); + } + + return completion; +} diff --git a/src/utils/matchers.ts b/src/utils/matchers.ts new file mode 100644 index 0000000..cf73b75 --- /dev/null +++ b/src/utils/matchers.ts @@ -0,0 +1,18 @@ +export function isInFilter( + line: string, +): RegExpMatchArray | null { + return line.match(/(add|remove|has|doing)_filter\([\s]*('|")[^"|']*$/); +} + +export function isInAction( + line: string, +): RegExpMatchArray | null { + return line.match(/(add|remove|has|doing|did)_action\([\s]*('|")[^"|']*$/); +} + +export function isInFunctionDeclaration( + line: string, +): RegExpMatchArray | null { + // add_ filter|action ( '" {hook} '" , + return line.match(/add_(?:filter|action)\(\s*['"](?\S+?)['"],\s*\w*?$/); +} diff --git a/src/utils/symbolHelpers.ts b/src/utils/symbolHelpers.ts new file mode 100644 index 0000000..497b510 --- /dev/null +++ b/src/utils/symbolHelpers.ts @@ -0,0 +1,35 @@ +import * as vscode from 'vscode'; +import { ContextualPosition } from '../types'; + +export function getContainingSymbol( + symbols: vscode.DocumentSymbol[], + position: vscode.Position, +) : ContextualPosition { + const inside = symbols.filter((symbol) => symbol.range.contains(position)); + const inNamespace = symbols.filter((symbol) => (vscode.SymbolKind.Namespace === symbol.kind)).length > 0; + + const context: ContextualPosition = { + symbol: null, + inNamespace, + inMethod: false, + inFunction: false, + }; + + if (!inside.length) { + return context; + } + + [context.symbol] = inside; + + if (context.symbol.children.length) { + const methods = context.symbol.children.filter((symbol) => symbol.range.contains(position)); + if (methods.length) { + [context.symbol] = methods; + } + } + + context.inMethod = (context.symbol.kind === vscode.SymbolKind.Method); + context.inFunction = (context.symbol.kind === vscode.SymbolKind.Function); + + return context; +} diff --git a/src/utils/typeHelpers.ts b/src/utils/typeHelpers.ts new file mode 100644 index 0000000..bc1d418 --- /dev/null +++ b/src/utils/typeHelpers.ts @@ -0,0 +1,93 @@ +import { Tag, TagType } from '../types'; + +export function getTagType( + tag: Tag, +): TagType | null { + // https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration + const allowedTypes: { [key: string]: number } = { + self: 5.0, + array: 5.1, + callable: 5.4, + bool: 7.0, + float: 7.0, + int: 7.0, + string: 7.0, + iterable: 7.1, + object: 7.2, + }; + + const typeData: TagType = { + type: '', + nullable: false, + }; + + // No type info? Bail. + if (!tag.types) { + return null; + } + + const types = [...tag.types]; + + // Handle nullable type. + if (types.length === 2) { + if (types[0] === 'null') { + types.splice(0, 1); + typeData.nullable = true; + } else if (types[1] === 'null') { + types.splice(1, 1); + typeData.nullable = true; + } + } + + // More than one type? Bail. + if (types.length !== 1) { + return null; + } + + let type = types[0]; + + // Un-hintable type? Bail. + if (['mixed'].includes(type)) { + return null; + } + + // Hinting for typed-arrays. + if (type.indexOf('[]') !== -1) { + type = 'array'; + } + + // Aliases for bool. + if (['false', 'true', 'boolean'].includes(type)) { + type = 'bool'; + } + + // Alias for callable. + if (type === 'callback') { + type = 'callable'; + } + + // Alias for int. + if (type === 'integer') { + type = 'int'; + } + + // Convert stdClass to object to avoid fatals when the stdClass gets promoted to a real class. + if (type === '\\stdClass') { + type = 'object'; + } + + // Check the allowed types, ignoring unknown types such as class and interface names. + if (allowedTypes[type]) { + return null; + } + + typeData.type = type; + + return typeData; +} + +export function getReturnType( + tag: Tag, +) : TagType | null { + return getTagType(tag); +} From 8b3bf6a1de8af4b9f9abef72546cefed4d404549 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 15:52:12 +0100 Subject: [PATCH 2/6] Fix linting errors - add eslint overrides for test files --- .eslintrc.json | 14 ++++- src/extension.ts | 6 +-- src/generators/docblockGenerator.ts | 4 +- src/generators/snippetGenerator.ts | 8 +-- src/providers/callbackCompletionProvider.ts | 40 +++++++------- src/test/suite/callbackCompletion.test.ts | 58 +++++++++----------- src/test/suite/edgeCases.test.ts | 60 ++++++++++----------- src/test/suite/hookCompletion.test.ts | 36 ++++++------- src/test/suite/hoverProvider.test.ts | 52 +++++++----------- 9 files changed, 130 insertions(+), 148 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 63c74a1..8607019 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,10 +17,22 @@ "indent": "off", "import/prefer-default-export": "off", "import/no-unresolved": "off", + "import/extensions": "off", "consistent-return": "off", "no-restricted-syntax": "off", "max-len":"off", "no-tabs": "off", "no-use-before-define": "off" - } + }, + "overrides": [ + { + "files": ["src/test/**/*.ts"], + "env": { + "mocha": true + }, + "rules": { + "no-promise-executor-return": "off" + } + } + ] } diff --git a/src/extension.ts b/src/extension.ts index b376ad0..7b7f9f2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; -import { createHookCompletionProvider } from './providers/hookCompletionProvider'; -import { createCallbackCompletionProvider } from './providers/callbackCompletionProvider'; -import { createHoverProvider } from './providers/hoverProvider'; +import { createHookCompletionProvider } from './providers/hookCompletionProvider.js'; +import { createCallbackCompletionProvider } from './providers/callbackCompletionProvider.js'; +import { createHoverProvider } from './providers/hoverProvider.js'; export function activate( context: vscode.ExtensionContext, diff --git a/src/generators/docblockGenerator.ts b/src/generators/docblockGenerator.ts index c1c145a..bcd66f9 100644 --- a/src/generators/docblockGenerator.ts +++ b/src/generators/docblockGenerator.ts @@ -1,9 +1,9 @@ -import { Tag } from '../types'; +import { Tag } from '../types/index.js'; export function generateDocblockLines( description: string, params: Tag[], - returnParam?: Tag + returnParam?: Tag, ): string[] { const docblockLines = [ '/**', diff --git a/src/generators/snippetGenerator.ts b/src/generators/snippetGenerator.ts index 664fd66..9225313 100644 --- a/src/generators/snippetGenerator.ts +++ b/src/generators/snippetGenerator.ts @@ -1,5 +1,5 @@ -import { Tag } from '../types'; -import { getTagType, getReturnType } from '../utils/typeHelpers'; +import { Tag } from '../types/index.js'; +import { getTagType, getReturnType } from '../utils/typeHelpers.js'; export interface CallbackSnippet { snippetCallback: string; @@ -10,7 +10,7 @@ export interface CallbackSnippet { export function generateCallbackSnippet( hookType: string, - params: Tag[] + params: Tag[], ): CallbackSnippet { const snippetArgsString = params.map((param) => { const val = `\\${param.variable}`; @@ -57,7 +57,7 @@ export function generateCallbackSnippet( snippetCallback, documentationCallback, returnTypeString, - suffix + suffix, }; } diff --git a/src/providers/callbackCompletionProvider.ts b/src/providers/callbackCompletionProvider.ts index 8779afc..bfe4eaa 100644 --- a/src/providers/callbackCompletionProvider.ts +++ b/src/providers/callbackCompletionProvider.ts @@ -1,14 +1,14 @@ import * as vscode from 'vscode'; -import { Hook } from '../types'; -import { getHook } from '../utils/hookHelpers'; -import { isInFunctionDeclaration } from '../utils/matchers'; -import { getContainingSymbol } from '../utils/symbolHelpers'; -import { generateDocblockLines } from '../generators/docblockGenerator'; +import { Hook } from '../types/index.js'; +import { getHook } from '../utils/hookHelpers.js'; +import { isInFunctionDeclaration } from '../utils/matchers.js'; +import { getContainingSymbol } from '../utils/symbolHelpers.js'; +import { generateDocblockLines } from '../generators/docblockGenerator.js'; import { generateCallbackSnippet, WORDPRESS_UTILITY_SNIPPETS, - SNIPPET_TYPES -} from '../generators/snippetGenerator'; + SNIPPET_TYPES, +} from '../generators/snippetGenerator.js'; const extensionName = 'vscode-wordpress-hooks'; @@ -20,7 +20,7 @@ function createClosureCompletion( suffix: string, lineLeadingWhitespace: string, position: vscode.Position, - docBlocksEnabled: boolean + docBlocksEnabled: boolean, ): vscode.CompletionItem { const completionItemForClosure = new vscode.CompletionItem('Closure', vscode.CompletionItemKind.Function); completionItemForClosure.insertText = new vscode.SnippetString(`function${snippetCallback}${suffix}`); @@ -46,7 +46,7 @@ function createArrowFunctionCompletion( docblockLines: string[], lineLeadingWhitespace: string, position: vscode.Position, - docBlocksEnabled: boolean + docBlocksEnabled: boolean, ): vscode.CompletionItem { const completionItemForArrow = new vscode.CompletionItem('Arrow function', vscode.CompletionItemKind.Function); @@ -67,7 +67,7 @@ function createArrowFunctionCompletion( } function createUtilityFunctionCompletions( - params: Array<{ types?: string[] }> + params: Array<{ types?: string[] }>, ): vscode.CompletionItem[] { const completions: vscode.CompletionItem[] = []; @@ -120,7 +120,7 @@ function createFunctionCompletion( docblockLines: string[], leadingWhitespace: string, insertionPosition: vscode.Position, - docBlocksEnabled: boolean + docBlocksEnabled: boolean, ): vscode.CompletionItem { const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; const completionItemForFunction = new vscode.CompletionItem('Function', vscode.CompletionItemKind.Function); @@ -156,7 +156,7 @@ function createMethodCompletion( docblockLines: string[], leadingWhitespace: string, methodInsertionPosition: vscode.Position, - docBlocksEnabled: boolean + docBlocksEnabled: boolean, ): vscode.CompletionItem { const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; const completionItemForMethod = new vscode.CompletionItem('Class method', vscode.CompletionItemKind.Method); @@ -209,7 +209,9 @@ export function createCallbackCompletionProvider(): vscode.Disposable { const params = hook.doc.tags.filter((tag) => tag.name === 'param'); // Generate callback snippet - const { snippetCallback, documentationCallback, returnTypeString, suffix } = generateCallbackSnippet(hook.type, params); + const { + snippetCallback, documentationCallback, returnTypeString, suffix, +} = generateCallbackSnippet(hook.type, params); const snippetArgsString = params.map((param) => `\\${param.variable}`).join(', '); const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); @@ -230,7 +232,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { suffix, lineLeadingWhitespace, position, - docBlocksEnabled + docBlocksEnabled, )); // Add arrow function and utility functions for filters @@ -244,7 +246,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { docblockLines, lineLeadingWhitespace, position, - docBlocksEnabled + docBlocksEnabled, )); completions.push(...createUtilityFunctionCompletions(params)); @@ -258,7 +260,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { vscode.window.activeTextEditor.document.uri, ) .then((symbols) => { - let insertionPosition = document.lineAt(position.line).range.end; + const insertionPosition = document.lineAt(position.line).range.end; if (symbols === undefined) { completions.push(createFunctionCompletion( @@ -268,7 +270,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { docblockLines, '', insertionPosition, - docBlocksEnabled + docBlocksEnabled, )); return completions; } @@ -292,7 +294,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { docblockLines, leadingWhitespace, positionContext.symbol.range.end, - docBlocksEnabled + docBlocksEnabled, )); } else { const functionName = `${hook.type}_${hook.name.replace(/[^a-z_]/g, '')}`; @@ -303,7 +305,7 @@ export function createCallbackCompletionProvider(): vscode.Disposable { docblockLines, leadingWhitespace, positionContext.symbol ? positionContext.symbol.range.end : insertionPosition, - docBlocksEnabled + docBlocksEnabled, ); if (positionContext.inNamespace) { diff --git a/src/test/suite/callbackCompletion.test.ts b/src/test/suite/callbackCompletion.test.ts index 8a39863..a43b11c 100644 --- a/src/test/suite/callbackCompletion.test.ts +++ b/src/test/suite/callbackCompletion.test.ts @@ -5,104 +5,96 @@ suite('Callback Completion Test Suite', () => { test('Should provide callback completions for add_action', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 20); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); // Should have standard callback types assert.ok( - labels.some(label => label === 'Closure' || label.includes('Closure')), - 'Should include Closure option' + labels.some((label) => label === 'Closure' || label.includes('Closure')), + 'Should include Closure option', ); assert.ok( - labels.some(label => label === 'Function' || label.includes('Function')), - 'Should include Function option' + labels.some((label) => label === 'Function' || label.includes('Function')), + 'Should include Function option', ); }); test('Should provide callback completions for add_filter', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 32); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); // Should have callback types including arrow function for filters assert.ok( - labels.some(label => label === 'Arrow function' || label.includes('Arrow')), - 'Should include Arrow function option for filters' + labels.some((label) => label === 'Arrow function' || label.includes('Arrow')), + 'Should include Arrow function option for filters', ); }); test('Should include WordPress utility functions for filters', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 32); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); // Check for WordPress utility functions - const hasUtilityFunctions = labels.some(label => - label && ( - label.includes('Return true') || - label.includes('Return false') || - label.includes('Return null') || - label.includes('return') - ) - ); + const hasUtilityFunctions = labels.some((label) => label && ( + label.includes('Return true') + || label.includes('Return false') + || label.includes('Return null') + || label.includes('return') + )); assert.ok( hasUtilityFunctions, - `Should include WordPress utility function options. Got: ${labels.slice(0, 15).join(', ')}` + `Should include WordPress utility function options. Got: ${labels.slice(0, 15).join(', ')}`, ); }); }); diff --git a/src/test/suite/edgeCases.test.ts b/src/test/suite/edgeCases.test.ts index 746b651..783e1e8 100644 --- a/src/test/suite/edgeCases.test.ts +++ b/src/test/suite/edgeCases.test.ts @@ -5,26 +5,24 @@ suite('Edge Cases Test Suite', () => { test('Should handle remove_action', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 16); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); assert.ok(labels.includes('init'), 'Should include actions for remove_action'); }); @@ -32,26 +30,24 @@ suite('Edge Cases Test Suite', () => { test('Should handle remove_filter', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 16); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); assert.ok(labels.includes('the_content'), 'Should include filters for remove_filter'); }); @@ -59,18 +55,18 @@ suite('Edge Cases Test Suite', () => { test('Should handle has_action', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 13); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); @@ -80,18 +76,18 @@ suite('Edge Cases Test Suite', () => { test('Should handle has_filter', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 13); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); @@ -101,18 +97,18 @@ suite('Edge Cases Test Suite', () => { test('Should handle doing_action', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 16); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); @@ -122,18 +118,18 @@ suite('Edge Cases Test Suite', () => { test('Should handle did_action', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 14); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); @@ -143,26 +139,24 @@ suite('Edge Cases Test Suite', () => { test('Should handle double quotes', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: 'add_action("' + content: 'add_action("', }); await vscode.window.showTextDocument(doc); - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(0, 12); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Should work with double quotes'); assert.ok(completions.items.length > 0, 'Should have completion items'); - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); assert.ok(labels.includes('init'), 'Should include actions with double quotes'); }); @@ -170,18 +164,18 @@ suite('Edge Cases Test Suite', () => { test('Should handle extra whitespace', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 16); const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Should handle extra whitespace'); diff --git a/src/test/suite/hookCompletion.test.ts b/src/test/suite/hookCompletion.test.ts index 3f0c55b..51ebc21 100644 --- a/src/test/suite/hookCompletion.test.ts +++ b/src/test/suite/hookCompletion.test.ts @@ -9,13 +9,13 @@ suite('Hook Completion Test Suite', () => { const content = " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); // Position is right after the opening quote const position = new vscode.Position(1, 12); @@ -24,28 +24,26 @@ suite('Hook Completion Test Suite', () => { const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); // Check that we have WordPress actions - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); // Should have "init" action assert.ok( labels.includes('init'), - `Should include "init" action. Got ${labels.length} items, first 10: ${labels.slice(0, 10).join(', ')}` + `Should include "init" action. Got ${labels.length} items, first 10: ${labels.slice(0, 10).join(', ')}`, ); }); test('Should provide autocomplete for add_filter', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " { const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); assert.ok(completions, 'Completions should be returned'); assert.ok(completions.items.length > 0, 'Should have completion items'); // Check for a common WordPress filter - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); assert.ok( - labels.some(label => label === 'the_content'), - 'Should include "the_content" filter' + labels.some((label) => label === 'the_content'), + 'Should include "the_content" filter', ); }); test('Should not provide hook completions outside of hook functions', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " { const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', doc.uri, - position + position, ); // Either no completions or the completions shouldn't be WordPress hooks if (completions && completions.items.length > 0) { - const labels = completions.items.map(item => - typeof item.label === 'string' ? item.label : item.label.label - ); + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); // Our extension shouldn't provide completions here // Other extensions might, so we just check our hooks aren't there assert.ok( - !labels.some(label => label === 'init' || label === 'wp_enqueue_scripts'), - 'Should not provide WordPress action completions in random strings' + !labels.some((label) => label === 'init' || label === 'wp_enqueue_scripts'), + 'Should not provide WordPress action completions in random strings', ); } }); diff --git a/src/test/suite/hoverProvider.test.ts b/src/test/suite/hoverProvider.test.ts index f8dc98f..c644082 100644 --- a/src/test/suite/hoverProvider.test.ts +++ b/src/test/suite/hoverProvider.test.ts @@ -5,11 +5,11 @@ suite('Hover Provider Test Suite', () => { test('Should provide hover information for actions', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); // Position over 'init' const position = new vscode.Position(1, 14); @@ -17,34 +17,30 @@ suite('Hover Provider Test Suite', () => { const hovers = await vscode.commands.executeCommand( 'vscode.executeHoverProvider', doc.uri, - position + position, ); assert.ok(hovers, 'Hover should be returned'); assert.ok(hovers.length > 0, 'Should have hover information'); - const hoverText = hovers.map(h => - h.contents.map(c => - typeof c === 'string' ? c : c.value - ).join('') - ).join(''); + const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); assert.ok( - hoverText.toLowerCase().includes('wordpress') || - hoverText.toLowerCase().includes('fires') || - hoverText.toLowerCase().includes('loaded'), - 'Hover should contain WordPress hook description' + hoverText.toLowerCase().includes('wordpress') + || hoverText.toLowerCase().includes('fires') + || hoverText.toLowerCase().includes('loaded'), + 'Hover should contain WordPress hook description', ); }); test('Should provide hover information for filters', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); // Position over 'the_content' const position = new vscode.Position(1, 18); @@ -52,54 +48,46 @@ suite('Hover Provider Test Suite', () => { const hovers = await vscode.commands.executeCommand( 'vscode.executeHoverProvider', doc.uri, - position + position, ); assert.ok(hovers, 'Hover should be returned'); assert.ok(hovers.length > 0, 'Should have hover information'); - const hoverText = hovers.map(h => - h.contents.map(c => - typeof c === 'string' ? c : c.value - ).join('') - ).join(''); + const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); assert.ok( - hoverText.toLowerCase().includes('filter') || - hoverText.toLowerCase().includes('content'), - 'Hover should contain filter description' + hoverText.toLowerCase().includes('filter') + || hoverText.toLowerCase().includes('content'), + 'Hover should contain filter description', ); }); test('Should include link to developer.wordpress.org', async () => { const doc = await vscode.workspace.openTextDocument({ language: 'php', - content: " setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); const position = new vscode.Position(1, 16); const hovers = await vscode.commands.executeCommand( 'vscode.executeHoverProvider', doc.uri, - position + position, ); assert.ok(hovers, 'Hover should be returned'); assert.ok(hovers.length > 0, 'Should have hover information'); - const hoverText = hovers.map(h => - h.contents.map(c => - typeof c === 'string' ? c : c.value - ).join('') - ).join(''); + const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); assert.ok( hoverText.includes('developer.wordpress.org'), - 'Hover should include link to WordPress developer docs' + 'Hover should include link to WordPress developer docs', ); }); }); From 7d606429e3b1b90758c2b2fc4c8f934a59aa00b0 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 16:57:00 +0100 Subject: [PATCH 3/6] Refactor tests to use matrix-based approach for all WordPress hook functions --- src/test/suite/callbackCompletion.test.ts | 151 +++++++----------- src/test/suite/edgeCases.test.ts | 184 ---------------------- src/test/suite/hookCompletion.test.ts | 122 +++++++------- src/test/suite/hoverProvider.test.ts | 166 +++++++++---------- 4 files changed, 206 insertions(+), 417 deletions(-) delete mode 100644 src/test/suite/edgeCases.test.ts diff --git a/src/test/suite/callbackCompletion.test.ts b/src/test/suite/callbackCompletion.test.ts index a43b11c..fa96141 100644 --- a/src/test/suite/callbackCompletion.test.ts +++ b/src/test/suite/callbackCompletion.test.ts @@ -2,99 +2,64 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; suite('Callback Completion Test Suite', () => { - test('Should provide callback completions for add_action', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " { + const isFilter = fn.includes('filter'); + + test(`Should provide callback completions for ${fn}`, async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: ` setTimeout(resolve, 100)); + + const pos = fn.length + hook.length + 7; + const position = new vscode.Position(1, pos); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position, + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); + + // Test common callback types + commonCallbackTypes.forEach(({ label }) => { + assert.ok( + labels.some((l) => l === label || l.includes(label)), + `Should include ${label} option for ${fn}`, + ); + }); + + // Test filter-specific types + if (isFilter) { + filterSpecificTypes.forEach(({ label }) => { + assert.ok( + labels.some((l) => l === label || l.includes(label)), + `Should include ${label} option for ${fn}`, + ); + }); + } }); - - await vscode.window.showTextDocument(doc); - await new Promise((resolve) => setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 20); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - // Should have standard callback types - assert.ok( - labels.some((label) => label === 'Closure' || label.includes('Closure')), - 'Should include Closure option', - ); - assert.ok( - labels.some((label) => label === 'Function' || label.includes('Function')), - 'Should include Function option', - ); - }); - - test('Should provide callback completions for add_filter', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 32); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - // Should have callback types including arrow function for filters - assert.ok( - labels.some((label) => label === 'Arrow function' || label.includes('Arrow')), - 'Should include Arrow function option for filters', - ); - }); - - test('Should include WordPress utility functions for filters', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 32); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - // Check for WordPress utility functions - const hasUtilityFunctions = labels.some((label) => label && ( - label.includes('Return true') - || label.includes('Return false') - || label.includes('Return null') - || label.includes('return') - )); - - assert.ok( - hasUtilityFunctions, - `Should include WordPress utility function options. Got: ${labels.slice(0, 15).join(', ')}`, - ); }); }); diff --git a/src/test/suite/edgeCases.test.ts b/src/test/suite/edgeCases.test.ts deleted file mode 100644 index 783e1e8..0000000 --- a/src/test/suite/edgeCases.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import * as assert from 'assert'; -import * as vscode from 'vscode'; - -suite('Edge Cases Test Suite', () => { - test('Should handle remove_action', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 16); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - assert.ok(labels.includes('init'), 'Should include actions for remove_action'); - }); - - test('Should handle remove_filter', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 16); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - assert.ok(labels.includes('the_content'), 'Should include filters for remove_filter'); - }); - - test('Should handle has_action', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 13); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - }); - - test('Should handle has_filter', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 13); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - }); - - test('Should handle doing_action', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 16); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - }); - - test('Should handle did_action', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 14); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - }); - - test('Should handle double quotes', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: 'add_action("', - }); - - await vscode.window.showTextDocument(doc); - await new Promise((resolve) => setTimeout(resolve, 100)); - - const position = new vscode.Position(0, 12); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Should work with double quotes'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - assert.ok(labels.includes('init'), 'Should include actions with double quotes'); - }); - - test('Should handle extra whitespace', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 16); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Should handle extra whitespace'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - }); -}); diff --git a/src/test/suite/hookCompletion.test.ts b/src/test/suite/hookCompletion.test.ts index 51ebc21..085db9a 100644 --- a/src/test/suite/hookCompletion.test.ts +++ b/src/test/suite/hookCompletion.test.ts @@ -4,67 +4,75 @@ import * as vscode from 'vscode'; suite('Hook Completion Test Suite', () => { vscode.window.showInformationMessage('Start hook completion tests.'); - test('Should provide autocomplete for add_action', async () => { - // Create a PHP document with content that triggers our completion provider - const content = " { + testVariations.forEach(({ quote, whitespace, description }) => { + test(`Should provide action completions for ${fn} with ${description}`, async () => { + const ws = whitespace || ''; + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: ` setTimeout(resolve, 100)); + + // Position is after the opening quote + const pos = fn.length + 1 + ws.length + 1; + const position = new vscode.Position(1, pos); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position, + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); + assert.ok(labels.includes('init'), `Should include init hook for ${fn}`); + }); }); - - await vscode.window.showTextDocument(doc); - - // Wait a bit for extension to activate - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Position is right after the opening quote - const position = new vscode.Position(1, 12); - - // Execute the completion provider - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - // Check that we have WordPress actions - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - - // Should have "init" action - assert.ok( - labels.includes('init'), - `Should include "init" action. Got ${labels.length} items, first 10: ${labels.slice(0, 10).join(', ')}`, - ); }); - test('Should provide autocomplete for add_filter', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " { + testVariations.forEach(({ quote, whitespace, description }) => { + test(`Should provide filter completions for ${fn} with ${description}`, async () => { + const ws = whitespace || ''; + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: ` setTimeout(resolve, 100)); + + // Position is after the opening quote + const pos = fn.length + 1 + ws.length + 1; + const position = new vscode.Position(1, pos); + + const completions = await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + doc.uri, + position, + ); + + assert.ok(completions, 'Completions should be returned'); + assert.ok(completions.items.length > 0, 'Should have completion items'); + + const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); + assert.ok(labels.includes('the_content'), `Should include the_content hook for ${fn}`); + }); }); - - await vscode.window.showTextDocument(doc); - - const position = new vscode.Position(1, 12); - - const completions = await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - doc.uri, - position, - ); - - assert.ok(completions, 'Completions should be returned'); - assert.ok(completions.items.length > 0, 'Should have completion items'); - - // Check for a common WordPress filter - const labels = completions.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label)); - assert.ok( - labels.some((label) => label === 'the_content'), - 'Should include "the_content" filter', - ); }); test('Should not provide hook completions outside of hook functions', async () => { diff --git a/src/test/suite/hoverProvider.test.ts b/src/test/suite/hoverProvider.test.ts index c644082..c1c87e8 100644 --- a/src/test/suite/hoverProvider.test.ts +++ b/src/test/suite/hoverProvider.test.ts @@ -2,92 +2,92 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; suite('Hover Provider Test Suite', () => { - test('Should provide hover information for actions', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " { + hookTestCases.forEach(({ hook, expectedTexts, exactMatch }) => { + test(`Should provide hover for ${fn} with ${hook}`, async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: ` setTimeout(resolve, 100)); + + const hoverPos = fn.length + 3 + Math.floor(hook.length / 2); + const position = new vscode.Position(1, hoverPos); + + const hovers = await vscode.commands.executeCommand( + 'vscode.executeHoverProvider', + doc.uri, + position, + ); + + assert.ok(hovers, 'Hover should be returned'); + assert.ok(hovers.length > 0, 'Should have hover information'); + + const hoverText = hovers + .map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')) + .join(''); + + if (exactMatch) { + assert.ok( + expectedTexts.some((text) => hoverText.includes(text)), + `Hover should contain one of: ${expectedTexts.join(', ')}`, + ); + } else { + assert.ok( + expectedTexts.some((text) => hoverText.toLowerCase().includes(text)), + `Hover should contain one of: ${expectedTexts.join(', ')}`, + ); + } + }); }); - - await vscode.window.showTextDocument(doc); - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Position over 'init' - const position = new vscode.Position(1, 14); - - const hovers = await vscode.commands.executeCommand( - 'vscode.executeHoverProvider', - doc.uri, - position, - ); - - assert.ok(hovers, 'Hover should be returned'); - assert.ok(hovers.length > 0, 'Should have hover information'); - - const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); - - assert.ok( - hoverText.toLowerCase().includes('wordpress') - || hoverText.toLowerCase().includes('fires') - || hoverText.toLowerCase().includes('loaded'), - 'Hover should contain WordPress hook description', - ); }); - test('Should provide hover information for filters', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " { + filterHookTestCases.forEach(({ hook, expectedTexts }) => { + test(`Should provide hover for ${fn} with ${hook}`, async () => { + const doc = await vscode.workspace.openTextDocument({ + language: 'php', + content: ` setTimeout(resolve, 100)); + + const hoverPos = fn.length + 3 + Math.floor(hook.length / 2); + const position = new vscode.Position(1, hoverPos); + + const hovers = await vscode.commands.executeCommand( + 'vscode.executeHoverProvider', + doc.uri, + position, + ); + + assert.ok(hovers, 'Hover should be returned'); + assert.ok(hovers.length > 0, 'Should have hover information'); + + const hoverText = hovers + .map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')) + .join(''); + + assert.ok( + expectedTexts.some((text) => hoverText.toLowerCase().includes(text)), + `Hover should contain one of: ${expectedTexts.join(', ')}`, + ); + }); }); - - await vscode.window.showTextDocument(doc); - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Position over 'the_content' - const position = new vscode.Position(1, 18); - - const hovers = await vscode.commands.executeCommand( - 'vscode.executeHoverProvider', - doc.uri, - position, - ); - - assert.ok(hovers, 'Hover should be returned'); - assert.ok(hovers.length > 0, 'Should have hover information'); - - const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); - - assert.ok( - hoverText.toLowerCase().includes('filter') - || hoverText.toLowerCase().includes('content'), - 'Hover should contain filter description', - ); - }); - - test('Should include link to developer.wordpress.org', async () => { - const doc = await vscode.workspace.openTextDocument({ - language: 'php', - content: " setTimeout(resolve, 100)); - - const position = new vscode.Position(1, 16); - - const hovers = await vscode.commands.executeCommand( - 'vscode.executeHoverProvider', - doc.uri, - position, - ); - - assert.ok(hovers, 'Hover should be returned'); - assert.ok(hovers.length > 0, 'Should have hover information'); - - const hoverText = hovers.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join('')).join(''); - - assert.ok( - hoverText.includes('developer.wordpress.org'), - 'Hover should include link to WordPress developer docs', - ); }); }); From c5b42abfc2665f14c8e05196d2c20ca406d500a6 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 17:18:35 +0100 Subject: [PATCH 4/6] Add comprehensive unit tests for utils and generators (208 total tests) --- src/test/suite/docblockGenerator.test.ts | 164 +++++++++++++++++ src/test/suite/hookHelpers.test.ts | 206 +++++++++++++++++++++ src/test/suite/matchers.test.ts | 134 ++++++++++++++ src/test/suite/snippetGenerator.test.ts | 217 +++++++++++++++++++++++ src/test/suite/symbolHelpers.test.ts | 148 ++++++++++++++++ src/test/suite/typeHelpers.test.ts | 130 ++++++++++++++ 6 files changed, 999 insertions(+) create mode 100644 src/test/suite/docblockGenerator.test.ts create mode 100644 src/test/suite/hookHelpers.test.ts create mode 100644 src/test/suite/matchers.test.ts create mode 100644 src/test/suite/snippetGenerator.test.ts create mode 100644 src/test/suite/symbolHelpers.test.ts create mode 100644 src/test/suite/typeHelpers.test.ts diff --git a/src/test/suite/docblockGenerator.test.ts b/src/test/suite/docblockGenerator.test.ts new file mode 100644 index 0000000..dbec109 --- /dev/null +++ b/src/test/suite/docblockGenerator.test.ts @@ -0,0 +1,164 @@ +import * as assert from 'assert'; +import { generateDocblockLines } from '../../generators/docblockGenerator'; +import { Tag } from '../../types'; + +suite('Docblock Generator Test Suite', () => { + suite('generateDocblockLines', () => { + test('Should generate basic docblock with description only', () => { + const description = 'My function description'; + const params: Tag[] = []; + + const lines = generateDocblockLines(description, params); + + assert.ok(lines.length > 0); + assert.strictEqual(lines[0], '/**'); + assert.strictEqual(lines[1], ` * ${description}`); + assert.strictEqual(lines[lines.length - 1], ' */'); + }); + + test('Should include param tags', () => { + const description = 'Function with parameters'; + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$content', content: 'The content' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLine = lines.find((line) => line.includes('@param')); + assert.ok(paramLine, 'Should have a @param line'); + assert.ok(paramLine?.includes('string')); + assert.ok(paramLine?.includes('$content')); + assert.ok(paramLine?.includes('The content')); + }); + + test('Should include multiple param tags', () => { + const description = 'Function with multiple parameters'; + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$content', content: 'The content' }, + { name: 'param', types: ['int'], variable: '$id', content: 'The ID' }, + { name: 'param', types: ['bool'], variable: '$force', content: 'Force update' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLines = lines.filter((line) => line.includes('@param')); + assert.strictEqual(paramLines.length, 3, 'Should have 3 @param lines'); + }); + + test('Should handle union types in params', () => { + const description = 'Function with union type'; + const params: Tag[] = [ + { name: 'param', types: ['string', 'int', 'null'], variable: '$value', content: 'The value' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLine = lines.find((line) => line.includes('@param')); + assert.ok(paramLine); + assert.ok(paramLine?.includes('string|int|null')); + }); + + test('Should include return tag when provided', () => { + const description = 'Function with return value'; + const params: Tag[] = []; + const returnParam: Tag = { + name: 'return', + types: ['bool'], + content: 'True on success', + }; + + const lines = generateDocblockLines(description, params, returnParam); + + const returnLine = lines.find((line) => line.includes('@return')); + assert.ok(returnLine, 'Should have a @return line'); + assert.ok(returnLine?.includes('bool')); + assert.ok(returnLine?.includes('True on success')); + }); + + test('Should align param types and names', () => { + const description = 'Function with aligned params'; + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$a', content: 'Short' }, + { name: 'param', types: ['int'], variable: '$longer_name', content: 'Longer' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLines = lines.filter((line) => line.includes('@param')); + assert.strictEqual(paramLines.length, 2); + + // Check that padding is applied (types and names should be padded) + assert.ok(paramLines[0].includes('string')); + assert.ok(paramLines[1].includes('int')); + }); + + test('Should handle params without types', () => { + const description = 'Function with untyped param'; + const params: Tag[] = [ + { name: 'param', variable: '$value', content: 'Some value' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLine = lines.find((line) => line.includes('@param')); + assert.ok(paramLine); + assert.ok(paramLine?.includes('$value')); + assert.ok(paramLine?.includes('Some value')); + }); + + test('Should handle params without variable names', () => { + const description = 'Function with unnamed param'; + const params: Tag[] = [ + { name: 'param', types: ['string'], content: 'Some value' }, + ]; + + const lines = generateDocblockLines(description, params); + + const paramLine = lines.find((line) => line.includes('@param')); + assert.ok(paramLine); + assert.ok(paramLine?.includes('string')); + }); + + test('Should generate complete docblock structure', () => { + const description = 'Complete function'; + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$content', content: 'The content' }, + ]; + const returnParam: Tag = { + name: 'return', + types: ['string'], + content: 'Modified content', + }; + + const lines = generateDocblockLines(description, params, returnParam); + + assert.strictEqual(lines[0], '/**'); + assert.ok(lines[1].includes('Complete function')); + assert.ok(lines.some((line) => line.includes('@param'))); + assert.ok(lines.some((line) => line.includes('@return'))); + assert.strictEqual(lines[lines.length - 1], ' */'); + }); + + test('Should handle empty description', () => { + const description = ''; + const params: Tag[] = []; + + const lines = generateDocblockLines(description, params); + + assert.strictEqual(lines[0], '/**'); + assert.strictEqual(lines[1], ' * '); + assert.strictEqual(lines[lines.length - 1], ' */'); + }); + + test('Should include blank line after description', () => { + const description = 'My function'; + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$value', content: 'Value' }, + ]; + + const lines = generateDocblockLines(description, params); + + assert.strictEqual(lines[2], ' *', 'Third line should be blank comment line'); + }); + }); +}); diff --git a/src/test/suite/hookHelpers.test.ts b/src/test/suite/hookHelpers.test.ts new file mode 100644 index 0000000..5812b41 --- /dev/null +++ b/src/test/suite/hookHelpers.test.ts @@ -0,0 +1,206 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { getHook, getHookSlug, getHookDescription, getHookCompletion } from '../../utils/hookHelpers'; + +suite('Hook Helpers Test Suite', () => { + suite('getHook', () => { + test('Should find common action hooks', () => { + const knownActions = ['init', 'wp_head', 'admin_init', 'wp_footer']; + + knownActions.forEach((hookName) => { + const hook = getHook(hookName); + assert.ok(hook, `Should find action: ${hookName}`); + assert.strictEqual(hook.name, hookName); + }); + }); + + test('Should find common filter hooks', () => { + const knownFilters = ['the_content', 'the_title', 'body_class']; + + knownFilters.forEach((hookName) => { + const hook = getHook(hookName); + assert.ok(hook, `Should find filter: ${hookName}`); + assert.strictEqual(hook.name, hookName); + }); + }); + + test('Should return undefined for non-existent hooks', () => { + const nonExistent = getHook('non_existent_hook_12345'); + assert.strictEqual(nonExistent, undefined); + }); + + test('Should find hooks by alias if they exist', () => { + // Try to find any hook with aliases and test it + const hook = getHook('init'); + if (hook && hook.aliases && hook.aliases.length > 0) { + const aliasHook = getHook(hook.aliases[0]); + assert.ok(aliasHook, 'Should find hook by alias'); + } + }); + + test('Should have required properties', () => { + const hook = getHook('init'); + assert.ok(hook); + assert.ok(hook.name); + assert.ok(hook.doc); + assert.ok(hook.doc.description); + }); + + test('Should check filters before actions', () => { + // If a name exists in both, should return the filter + const hook = getHook('the_content'); + assert.ok(hook); + // the_content is a filter, so it should be found first + assert.strictEqual(hook.name, 'the_content'); + }); + }); + + suite('getHookSlug', () => { + test('Should convert hook name to slug', () => { + const tests = [ + { hook: { name: 'init', doc: {} } as any, expected: 'init' }, + { hook: { name: 'wp_head', doc: {} } as any, expected: 'wp_head' }, + { hook: { name: 'the_content', doc: {} } as any, expected: 'the_content' }, + ]; + + tests.forEach(({ hook, expected }) => { + const slug = getHookSlug(hook); + assert.strictEqual(slug, expected); + }); + }); + + test('Should remove invalid characters', () => { + const tests = [ + { hook: { name: 'hook@123', doc: {} } as any, expected: 'hook' }, + { hook: { name: 'hook#name', doc: {} } as any, expected: 'hookname' }, + { hook: { name: 'hook name', doc: {} } as any, expected: 'hookname' }, + ]; + + tests.forEach(({ hook, expected }) => { + const slug = getHookSlug(hook); + assert.strictEqual(slug, expected); + }); + }); + + test('Should preserve underscores and hyphens', () => { + const hook = { name: 'my-hook_name', doc: {} } as any; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'my-hook_name'); + }); + + test('Should convert to lowercase', () => { + const hook = { name: 'MyHook', doc: {} } as any; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'myhook'); + }); + }); + + suite('getHookDescription', () => { + test('Should return MarkdownString', () => { + const hook = getHook('init'); + assert.ok(hook); + + const description = getHookDescription(hook); + assert.ok(description instanceof vscode.MarkdownString); + }); + + test('Should include long description', () => { + const hook = getHook('init'); + assert.ok(hook); + + const description = getHookDescription(hook); + const value = description.value; + + assert.ok(value.length > 0); + assert.ok(value.includes(hook.doc.long_description || hook.doc.description)); + }); + + test('Should include developer.wordpress.org link', () => { + const hook = getHook('init'); + assert.ok(hook); + + const description = getHookDescription(hook); + assert.ok(description.value.includes('developer.wordpress.org')); + assert.ok(description.value.includes('/reference/hooks/')); + }); + + test('Should include param tags if present', () => { + const hook = getHook('the_content'); + assert.ok(hook); + + const description = getHookDescription(hook); + const params = hook.doc.tags?.filter((tag) => tag.name === 'param'); + + if (params && params.length > 0) { + assert.ok(description.value.includes('@param')); + } + }); + + test('Should format param tags with types and variables', () => { + const hook = getHook('the_content'); + assert.ok(hook); + + const description = getHookDescription(hook); + const params = hook.doc.tags?.filter((tag) => tag.name === 'param'); + + if (params && params.length > 0 && params[0].variable) { + assert.ok(description.value.includes(params[0].variable)); + } + }); + }); + + suite('getHookCompletion', () => { + test('Should return CompletionItem', () => { + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + assert.ok(completion instanceof vscode.CompletionItem); + }); + + test('Should set label to hook name', () => { + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + assert.strictEqual(completion.label, 'init'); + }); + + test('Should set detail to description', () => { + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + assert.strictEqual(completion.detail, hook.doc.description); + }); + + test('Should set documentation to formatted description', () => { + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + assert.ok(completion.documentation instanceof vscode.MarkdownString); + }); + + test('Should set filterText to aliases if present', () => { + // Find a hook with aliases + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + + if (hook.aliases && hook.aliases.length > 0) { + assert.ok(completion.filterText); + assert.ok(completion.filterText?.includes(hook.aliases[0])); + } + }); + + test('Should set kind to Value', () => { + const hook = getHook('init'); + assert.ok(hook); + + const completion = getHookCompletion(hook); + assert.strictEqual(completion.kind, vscode.CompletionItemKind.Value); + }); + }); +}); diff --git a/src/test/suite/matchers.test.ts b/src/test/suite/matchers.test.ts new file mode 100644 index 0000000..40c962f --- /dev/null +++ b/src/test/suite/matchers.test.ts @@ -0,0 +1,134 @@ +import * as assert from 'assert'; +import { isInFilter, isInAction, isInFunctionDeclaration } from '../../utils/matchers'; + +suite('Matchers Test Suite', () => { + suite('isInAction', () => { + const actionFunctions = ['add_action', 'remove_action', 'has_action', 'doing_action', 'did_action']; + const quotes = ["'", '"']; + const whitespaceVariations = ['', ' ', ' ', '\t']; + + actionFunctions.forEach((fn) => { + quotes.forEach((quote) => { + whitespaceVariations.forEach((ws) => { + test(`Should match ${fn} with ${quote === "'" ? 'single' : 'double'} quotes and whitespace: "${ws}"`, () => { + const line = `${fn}(${ws}${quote}`; + const match = isInAction(line); + assert.ok(match, `Should match: ${line}`); + }); + }); + }); + }); + + test('Should match action with partial hook name', () => { + assert.ok(isInAction("add_action('ini")); + assert.ok(isInAction("remove_action('wp_he")); + }); + + test('Should not match filter functions', () => { + assert.strictEqual(isInAction("add_filter('"), null); + assert.strictEqual(isInAction("remove_filter('"), null); + }); + + test('Should not match incomplete syntax', () => { + assert.strictEqual(isInAction('add_action'), null); + assert.strictEqual(isInAction('add_action('), null); + }); + + test('Should not match non-hook functions', () => { + assert.strictEqual(isInAction("some_function('"), null); + assert.strictEqual(isInAction("my_action('"), null); + }); + }); + + suite('isInFilter', () => { + const filterFunctions = ['add_filter', 'remove_filter', 'has_filter', 'doing_filter']; + const quotes = ["'", '"']; + const whitespaceVariations = ['', ' ', ' ', '\t']; + + filterFunctions.forEach((fn) => { + quotes.forEach((quote) => { + whitespaceVariations.forEach((ws) => { + test(`Should match ${fn} with ${quote === "'" ? 'single' : 'double'} quotes and whitespace: "${ws}"`, () => { + const line = `${fn}(${ws}${quote}`; + const match = isInFilter(line); + assert.ok(match, `Should match: ${line}`); + }); + }); + }); + }); + + test('Should match filter with partial hook name', () => { + assert.ok(isInFilter("add_filter('the_")); + assert.ok(isInFilter("remove_filter('post_")); + }); + + test('Should not match action functions', () => { + assert.strictEqual(isInFilter("add_action('"), null); + assert.strictEqual(isInFilter("did_action('"), null); + }); + + test('Should not match incomplete syntax', () => { + assert.strictEqual(isInFilter('add_filter'), null); + assert.strictEqual(isInFilter('add_filter('), null); + }); + + test('Should not match non-hook functions', () => { + assert.strictEqual(isInFilter("some_function('"), null); + assert.strictEqual(isInFilter("my_filter('"), null); + }); + }); + + suite('isInFunctionDeclaration', () => { + test('Should match add_action with callback parameter', () => { + const match = isInFunctionDeclaration("add_action('init', my"); + assert.ok(match); + assert.strictEqual(match?.groups?.hook, 'init'); + }); + + test('Should match add_filter with callback parameter', () => { + const match = isInFunctionDeclaration("add_filter('the_content', my"); + assert.ok(match); + assert.strictEqual(match?.groups?.hook, 'the_content'); + }); + + test('Should match with double quotes', () => { + const match = isInFunctionDeclaration('add_action("init", my'); + assert.ok(match); + assert.strictEqual(match?.groups?.hook, 'init'); + }); + + test('Should match with whitespace variations', () => { + assert.ok(isInFunctionDeclaration("add_action( 'init', my")); + assert.ok(isInFunctionDeclaration("add_filter(\t'the_content',\tmy")); + }); + + test('Should capture hook name in groups', () => { + const tests = [ + { line: "add_action('wp_head', ", expected: 'wp_head' }, + { line: "add_filter('the_title', ", expected: 'the_title' }, + { line: "add_action('custom_hook', ", expected: 'custom_hook' }, + ]; + + tests.forEach(({ line, expected }) => { + const match = isInFunctionDeclaration(line); + assert.ok(match, `Should match: ${line}`); + assert.strictEqual(match?.groups?.hook, expected); + }); + }); + + test('Should not match without callback parameter', () => { + assert.strictEqual(isInFunctionDeclaration("add_action('init'"), null); + assert.strictEqual(isInFunctionDeclaration("add_filter('the_content'"), null); + }); + + test('Should not match incomplete syntax', () => { + assert.strictEqual(isInFunctionDeclaration('add_action'), null); + assert.strictEqual(isInFunctionDeclaration("add_action('"), null); + }); + + test('Should not match non-add functions', () => { + assert.strictEqual(isInFunctionDeclaration("remove_action('init', "), null); + assert.strictEqual(isInFunctionDeclaration("has_filter('the_content', "), null); + }); + }); +}); diff --git a/src/test/suite/snippetGenerator.test.ts b/src/test/suite/snippetGenerator.test.ts new file mode 100644 index 0000000..bc77e32 --- /dev/null +++ b/src/test/suite/snippetGenerator.test.ts @@ -0,0 +1,217 @@ +import * as assert from 'assert'; +import { generateCallbackSnippet, WORDPRESS_UTILITY_SNIPPETS, SNIPPET_TYPES } from '../../generators/snippetGenerator'; +import { Tag } from '../../types'; + +suite('Snippet Generator Test Suite', () => { + suite('generateCallbackSnippet', () => { + test('Should generate action callback with no parameters', () => { + const hookType = 'action'; + const params: Tag[] = []; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('void')); + assert.ok(snippet.snippetCallback.includes('${1}')); + assert.strictEqual(snippet.suffix, ' '); + }); + + test('Should generate action callback with single parameter', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$post', types: ['WP_Post'], content: 'The post' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('WP_Post \\$post')); + assert.ok(snippet.snippetCallback.includes('void')); + assert.strictEqual(snippet.suffix, ' '); + }); + + test('Should generate action callback with multiple parameters', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$post', types: ['WP_Post'], content: 'The post' }, + { name: 'param', variable: '$id', types: ['int'], content: 'The ID' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('WP_Post \\$post')); + assert.ok(snippet.snippetCallback.includes('\\$id')); + assert.strictEqual(snippet.suffix, ', 10, 2 '); + }); + + test('Should generate filter callback with return statement', () => { + const hookType = 'filter'; + const params: Tag[] = [ + { name: 'param', variable: '$content', types: ['WP_Post'], content: 'The content' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('return \\$content')); + assert.ok(snippet.returnTypeString.includes('WP_Post')); + }); + + test('Should handle nullable return types for filters', () => { + const hookType = 'filter'; + const params: Tag[] = [ + { name: 'param', variable: '$value', types: ['null', 'WP_Post'], content: 'The value' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.returnTypeString.includes('?WP_Post')); + }); + + test('Should handle filter_reference hook type', () => { + const hookType = 'filter_reference'; + const params: Tag[] = [ + { name: 'param', variable: '$data', types: ['array'], content: 'The data' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('return \\$data')); + }); + + test('Should handle parameters without type hints', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$value', content: 'Some value' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('\\$value')); + assert.ok(!snippet.snippetCallback.includes('WP_Post')); + }); + + test('Should handle nullable parameter types', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$post', types: ['null', 'WP_Post'], content: 'The post' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('?WP_Post \\$post')); + }); + + test('Should escape dollar signs in snippet', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$value', types: ['string'], content: 'Value' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + // Snippet should have escaped dollar signs + assert.ok(snippet.snippetCallback.includes('\\$value')); + // Documentation should not have escaped dollar signs + assert.ok(snippet.documentationCallback.includes('$value')); + assert.ok(!snippet.documentationCallback.includes('\\$value')); + }); + + test('Should set correct suffix for single parameter', () => { + const hookType = 'action'; + const params: Tag[] = [ + { name: 'param', variable: '$value', types: ['string'], content: 'Value' }, + ]; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.strictEqual(snippet.suffix, ' '); + }); + + test('Should set correct suffix for multiple parameters', () => { + const tests = [ + { count: 2, expected: ', 10, 2 ' }, + { count: 3, expected: ', 10, 3 ' }, + { count: 5, expected: ', 10, 5 ' }, + ]; + + tests.forEach(({ count, expected }) => { + const params: Tag[] = Array(count).fill(null).map((_, i) => ({ + name: 'param', + variable: `$param${i}`, + types: ['string'], + content: 'Param', + })); + + const snippet = generateCallbackSnippet('action', params); + assert.strictEqual(snippet.suffix, expected); + }); + }); + + test('Should include placeholder in snippet callback', () => { + const hookType = 'action'; + const params: Tag[] = []; + + const snippet = generateCallbackSnippet(hookType, params); + + assert.ok(snippet.snippetCallback.includes('${1}')); + }); + }); + + suite('WORDPRESS_UTILITY_SNIPPETS', () => { + test('Should contain expected utility functions', () => { + const expectedKeys = [ + '__return_true', + '__return_false', + '__return_zero', + '__return_empty_array', + '__return_empty_string', + ]; + + expectedKeys.forEach((key) => { + assert.ok(key in WORDPRESS_UTILITY_SNIPPETS, `Should have ${key}`); + }); + }); + + test('Should have descriptive labels', () => { + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_true, 'Return true'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_false, 'Return false'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_zero, 'Return zero'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_empty_array, 'Return empty array'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_empty_string, 'Return empty string'); + }); + }); + + suite('SNIPPET_TYPES', () => { + test('Should map bool to true/false snippets', () => { + assert.ok(SNIPPET_TYPES.bool.includes('__return_true')); + assert.ok(SNIPPET_TYPES.bool.includes('__return_false')); + }); + + test('Should map numeric types to zero snippet', () => { + assert.ok(SNIPPET_TYPES.int.includes('__return_zero')); + assert.ok(SNIPPET_TYPES.float.includes('__return_zero')); + }); + + test('Should map string to empty string snippet', () => { + assert.ok(SNIPPET_TYPES.string.includes('__return_empty_string')); + }); + + test('Should map array types to empty array snippet', () => { + assert.ok(SNIPPET_TYPES.array.includes('__return_empty_array')); + assert.ok(SNIPPET_TYPES.iterable.includes('__return_empty_array')); + }); + + test('Should have empty arrays for unsupported types', () => { + assert.strictEqual(SNIPPET_TYPES.null.length, 0); + assert.strictEqual(SNIPPET_TYPES.self.length, 0); + assert.strictEqual(SNIPPET_TYPES.callable.length, 0); + assert.strictEqual(SNIPPET_TYPES.object.length, 0); + }); + + test('Should contain all expected type mappings', () => { + const expectedTypes = ['null', 'self', 'array', 'callable', 'bool', 'float', 'int', 'string', 'iterable', 'object']; + + expectedTypes.forEach((type) => { + assert.ok(type in SNIPPET_TYPES, `Should have mapping for ${type}`); + }); + }); + }); +}); diff --git a/src/test/suite/symbolHelpers.test.ts b/src/test/suite/symbolHelpers.test.ts new file mode 100644 index 0000000..7d252b7 --- /dev/null +++ b/src/test/suite/symbolHelpers.test.ts @@ -0,0 +1,148 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { getContainingSymbol } from '../../utils/symbolHelpers'; + +suite('Symbol Helpers Test Suite', () => { + suite('getContainingSymbol', () => { + function createSymbol( + name: string, + kind: vscode.SymbolKind, + range: vscode.Range, + children: vscode.DocumentSymbol[] = [], + ): vscode.DocumentSymbol { + return { + name, + kind, + range, + selectionRange: range, + children, + } as vscode.DocumentSymbol; + } + + test('Should return default context when no symbols contain position', () => { + const symbols = [ + createSymbol('test', vscode.SymbolKind.Function, new vscode.Range(0, 0, 5, 0)), + ]; + const position = new vscode.Position(10, 0); + + const context = getContainingSymbol(symbols, position); + + assert.strictEqual(context.symbol, null); + assert.strictEqual(context.inFunction, false); + assert.strictEqual(context.inMethod, false); + assert.strictEqual(context.inNamespace, false); + }); + + test('Should detect when inside a function', () => { + const symbols = [ + createSymbol('myFunction', vscode.SymbolKind.Function, new vscode.Range(0, 0, 10, 0)), + ]; + const position = new vscode.Position(5, 0); + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'myFunction'); + assert.strictEqual(context.inFunction, true); + assert.strictEqual(context.inMethod, false); + }); + + test('Should detect when inside a method', () => { + const method = createSymbol('myMethod', vscode.SymbolKind.Method, new vscode.Range(2, 0, 8, 0)); + const classSymbol = createSymbol('MyClass', vscode.SymbolKind.Class, new vscode.Range(0, 0, 10, 0), [method]); + + const symbols = [classSymbol]; + const position = new vscode.Position(5, 0); + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'myMethod'); + assert.strictEqual(context.inMethod, true); + assert.strictEqual(context.inFunction, false); + }); + + test('Should detect namespace presence', () => { + const symbols = [ + createSymbol('MyNamespace', vscode.SymbolKind.Namespace, new vscode.Range(0, 0, 20, 0)), + ]; + const position = new vscode.Position(5, 0); + + const context = getContainingSymbol(symbols, position); + + assert.strictEqual(context.inNamespace, true); + }); + + test('Should return class symbol when position is in class but not in method', () => { + const classSymbol = createSymbol('MyClass', vscode.SymbolKind.Class, new vscode.Range(0, 0, 10, 0), [ + createSymbol('myMethod', vscode.SymbolKind.Method, new vscode.Range(2, 0, 4, 0)), + ]); + + const symbols = [classSymbol]; + const position = new vscode.Position(5, 0); // After the method + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'MyClass'); + assert.strictEqual(context.inMethod, false); + assert.strictEqual(context.inFunction, false); + }); + + test('Should handle nested symbols correctly', () => { + const innerMethod = createSymbol('innerMethod', vscode.SymbolKind.Method, new vscode.Range(5, 2, 8, 2)); + const outerClass = createSymbol('OuterClass', vscode.SymbolKind.Class, new vscode.Range(0, 0, 10, 0), [ + innerMethod, + ]); + + const symbols = [outerClass]; + const position = new vscode.Position(6, 0); // Inside innerMethod + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'innerMethod'); + assert.strictEqual(context.inMethod, true); + }); + + test('Should handle multiple top-level symbols', () => { + const symbols = [ + createSymbol('function1', vscode.SymbolKind.Function, new vscode.Range(0, 0, 5, 0)), + createSymbol('function2', vscode.SymbolKind.Function, new vscode.Range(6, 0, 10, 0)), + createSymbol('function3', vscode.SymbolKind.Function, new vscode.Range(11, 0, 15, 0)), + ]; + const position = new vscode.Position(7, 0); // Inside function2 + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'function2'); + assert.strictEqual(context.inFunction, true); + }); + + test('Should return first matching symbol when multiple overlap', () => { + const symbols = [ + createSymbol('symbol1', vscode.SymbolKind.Function, new vscode.Range(0, 0, 10, 0)), + createSymbol('symbol2', vscode.SymbolKind.Function, new vscode.Range(0, 0, 10, 0)), + ]; + const position = new vscode.Position(5, 0); + + const context = getContainingSymbol(symbols, position); + + assert.ok(context.symbol); + assert.strictEqual(context.symbol.name, 'symbol1'); + }); + + test('Should handle empty symbol array', () => { + const symbols: vscode.DocumentSymbol[] = []; + const position = new vscode.Position(0, 0); + + const context = getContainingSymbol(symbols, position); + + assert.strictEqual(context.symbol, null); + assert.strictEqual(context.inFunction, false); + assert.strictEqual(context.inMethod, false); + assert.strictEqual(context.inNamespace, false); + }); + }); +}); diff --git a/src/test/suite/typeHelpers.test.ts b/src/test/suite/typeHelpers.test.ts new file mode 100644 index 0000000..3fe7d89 --- /dev/null +++ b/src/test/suite/typeHelpers.test.ts @@ -0,0 +1,130 @@ +import * as assert from 'assert'; +import { getTagType, getReturnType } from '../../utils/typeHelpers'; +import { Tag } from '../../types'; + +suite('Type Helpers Test Suite', () => { + suite('getTagType', () => { + test('Should return null when tag has no types', () => { + const tag: Tag = { name: 'param', content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null); + }); + + test('Should return null for mixed type', () => { + const tag: Tag = { name: 'param', types: ['mixed'], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null); + }); + + test('Should return null for allowed PHP types', () => { + const allowedTypes = ['self', 'array', 'callable', 'bool', 'float', 'int', 'string', 'iterable', 'object']; + + allowedTypes.forEach((type) => { + const tag: Tag = { name: 'param', types: [type], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, `Should return null for type: ${type}`); + }); + }); + + test('Should handle nullable types', () => { + const tests = [ + { types: ['null', 'WP_Post'], expectedNullable: true, expectedType: 'WP_Post' }, + { types: ['WP_User', 'null'], expectedNullable: true, expectedType: 'WP_User' }, + ]; + + tests.forEach(({ types, expectedNullable, expectedType }) => { + const tag: Tag = { name: 'param', types, content: 'test' }; + const result = getTagType(tag); + assert.ok(result, `Should return result for types: ${types.join(', ')}`); + assert.strictEqual(result?.nullable, expectedNullable); + assert.strictEqual(result?.type, expectedType); + }); + }); + + test('Should return null for multiple non-nullable types', () => { + const tag: Tag = { name: 'param', types: ['WP_Post', 'WP_User'], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, 'Should return null for union types'); + }); + + test('Should convert typed arrays to array', () => { + const tests = ['WP_Post[]', 'string[]', 'int[]']; + + tests.forEach((type) => { + const tag: Tag = { name: 'param', types: [type], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, `Typed array ${type} should convert to array which is an allowed type`); + }); + }); + + test('Should convert boolean aliases to bool', () => { + const boolAliases = ['false', 'true', 'boolean']; + + boolAliases.forEach((alias) => { + const tag: Tag = { name: 'param', types: [alias], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, `${alias} should convert to bool which is an allowed type`); + }); + }); + + test('Should convert callback to callable', () => { + const tag: Tag = { name: 'param', types: ['callback'], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, 'callback should convert to callable which is an allowed type'); + }); + + test('Should convert integer to int', () => { + const tag: Tag = { name: 'param', types: ['integer'], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, 'integer should convert to int which is an allowed type'); + }); + + test('Should convert \\stdClass to object', () => { + const tag: Tag = { name: 'param', types: ['\\stdClass'], content: 'test' }; + const result = getTagType(tag); + assert.strictEqual(result, null, '\\stdClass should convert to object which is an allowed type'); + }); + + test('Should return class names as custom types', () => { + const classNames = ['WP_Post', 'WP_User', 'WP_Query', 'Custom_Class']; + + classNames.forEach((className) => { + const tag: Tag = { name: 'param', types: [className], content: 'test' }; + const result = getTagType(tag); + assert.ok(result, `Should return result for class: ${className}`); + assert.strictEqual(result?.type, className); + assert.strictEqual(result?.nullable, false); + }); + }); + + test('Should handle namespaced class names', () => { + const tag: Tag = { name: 'param', types: ['\\Namespace\\ClassName'], content: 'test' }; + const result = getTagType(tag); + assert.ok(result); + assert.strictEqual(result?.type, '\\Namespace\\ClassName'); + }); + }); + + suite('getReturnType', () => { + test('Should delegate to getTagType', () => { + const tag: Tag = { name: 'return', types: ['WP_Post'], content: 'test' }; + const result = getReturnType(tag); + assert.ok(result); + assert.strictEqual(result?.type, 'WP_Post'); + }); + + test('Should return null for no types', () => { + const tag: Tag = { name: 'return', content: 'test' }; + const result = getReturnType(tag); + assert.strictEqual(result, null); + }); + + test('Should handle nullable return types', () => { + const tag: Tag = { name: 'return', types: ['null', 'WP_Post'], content: 'test' }; + const result = getReturnType(tag); + assert.ok(result); + assert.strictEqual(result?.nullable, true); + assert.strictEqual(result?.type, 'WP_Post'); + }); + }); +}); From 668b02a7f34620173894f6fc5770eed81ffc6200 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 18:30:07 +0100 Subject: [PATCH 5/6] Improve test coverage - add alignment verification for docblock generator --- src/test/suite/docblockGenerator.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/suite/docblockGenerator.test.ts b/src/test/suite/docblockGenerator.test.ts index dbec109..c8ee0c7 100644 --- a/src/test/suite/docblockGenerator.test.ts +++ b/src/test/suite/docblockGenerator.test.ts @@ -87,9 +87,13 @@ suite('Docblock Generator Test Suite', () => { const paramLines = lines.filter((line) => line.includes('@param')); assert.strictEqual(paramLines.length, 2); - // Check that padding is applied (types and names should be padded) - assert.ok(paramLines[0].includes('string')); - assert.ok(paramLines[1].includes('int')); + // Verify padding: variable names should start at the same column + const index1 = paramLines[0].indexOf('$a'); + const index2 = paramLines[1].indexOf('$longer_name'); + assert.strictEqual(index1, index2, 'Variable names should be aligned at same column'); + + // The first line should have padding after 'string' to align with 'int' + assert.ok(paramLines[0].match(/@param\s+string\s+\$a/), 'Should have padding between type and variable'); }); test('Should handle params without types', () => { From dd5d538d01c0a1655fad3d196110c9b6c616e463 Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Wed, 1 Oct 2025 20:35:41 +0100 Subject: [PATCH 6/6] Add mutation testing with Stryker and refactor for testability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor codebase to separate pure logic from VS Code adapters - Create src/core/ directory with framework-agnostic code - Add Stryker mutation testing configuration - Add 51 unit tests for core modules - Achieve 63.70% mutation score (up from 5.48%) Test improvements: - typeHelpers: 0% → 62.50% (45 mutants killed) - snippetGenerator: 0% → 73.33% (55 mutants killed) - docblockGenerator: 0% → 62.79% (27 mutants killed) - hookHelpers: 0% → 56.58% (43 mutants killed) - matchers: 61.54% maintained (16 mutants killed) All 208 existing tests pass with zero regressions. 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/test.yml | 3 + .gitignore | 2 + .mocharc.unit.json | 6 + package-lock.json | 3195 +++++++++++++++++- package.json | 7 +- src/core/docblockGenerator.ts | 42 + src/core/hookHelpers.ts | 101 + src/core/matchers.ts | 18 + src/core/snippetGenerator.ts | 83 + src/core/typeHelpers.ts | 93 + src/generators/docblockGenerator.ts | 46 +- src/generators/snippetGenerator.ts | 90 +- src/test/unit/core-docblockGenerator.test.ts | 110 + src/test/unit/core-hookHelpers.test.ts | 140 + src/test/unit/core-matchers.test.ts | 100 + src/test/unit/core-snippetGenerator.test.ts | 99 + src/test/unit/core-typeHelpers.test.ts | 72 + src/utils/hookHelpers.ts | 87 +- src/utils/matchers.ts | 24 +- src/utils/typeHelpers.ts | 98 +- stryker.config.json | 34 + 21 files changed, 4066 insertions(+), 384 deletions(-) create mode 100644 .mocharc.unit.json create mode 100644 src/core/docblockGenerator.ts create mode 100644 src/core/hookHelpers.ts create mode 100644 src/core/matchers.ts create mode 100644 src/core/snippetGenerator.ts create mode 100644 src/core/typeHelpers.ts create mode 100644 src/test/unit/core-docblockGenerator.test.ts create mode 100644 src/test/unit/core-hookHelpers.test.ts create mode 100644 src/test/unit/core-matchers.test.ts create mode 100644 src/test/unit/core-snippetGenerator.test.ts create mode 100644 src/test/unit/core-typeHelpers.test.ts create mode 100644 stryker.config.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0bf3e7..ac22b5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,3 +33,6 @@ jobs: - name: Run tests run: xvfb-run -a npm test + + - name: Run mutation testing + run: npm run test:mutation diff --git a/.gitignore b/.gitignore index 4562123..849d428 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ node_modules .vscode-test/ *.vsix /out +.stryker-tmp/ +reports/ diff --git a/.mocharc.unit.json b/.mocharc.unit.json new file mode 100644 index 0000000..ea4a7d8 --- /dev/null +++ b/.mocharc.unit.json @@ -0,0 +1,6 @@ +{ + "spec": "out/test/unit/**/*.test.js", + "ui": "tdd", + "timeout": 5000, + "color": true +} diff --git a/package-lock.json b/package-lock.json index 102e20f..d67381a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,9 @@ "@wp-hooks/wordpress-core": "^1.10.0" }, "devDependencies": { + "@stryker-mutator/core": "^9.1.1", + "@stryker-mutator/mocha-runner": "^9.1.1", + "@stryker-mutator/typescript-checker": "^9.1.1", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.10", "@types/node": "^20", @@ -259,6 +262,567 @@ "node": ">=16" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -266,94 +830,514 @@ "dev": true, "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", + "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", + "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.1", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", + "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz", + "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.4.tgz", + "integrity": "sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.18.tgz", + "integrity": "sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz", + "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz", + "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/external-editor": "^1.0.2", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "node_modules/@inquirer/expand": { + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.20.tgz", + "integrity": "sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==", "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "node_modules/@inquirer/external-editor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "chardet": "^2.1.0", + "iconv-lite": "^0.7.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@eslint/js": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", - "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==", + "node_modules/@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "node_modules/@inquirer/input": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.4.tgz", + "integrity": "sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.20.tgz", + "integrity": "sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==", "dev": true, + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" }, "engines": { - "node": ">=10.10.0" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@inquirer/password": { + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.20.tgz", + "integrity": "sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.6.tgz", + "integrity": "sha512-68JhkiojicX9SBUD8FE/pSKbOKtwoyaVj1kwqLfvjlVXZvOy3iaSWX4dCLsZyYx/5Ur07Fq+yuDNOen+5ce6ig==", "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.2.4", + "@inquirer/confirm": "^5.1.18", + "@inquirer/editor": "^4.2.20", + "@inquirer/expand": "^4.0.20", + "@inquirer/input": "^4.2.4", + "@inquirer/number": "^3.0.20", + "@inquirer/password": "^4.0.20", + "@inquirer/rawlist": "^4.1.8", + "@inquirer/search": "^3.1.3", + "@inquirer/select": "^4.3.4" + }, "engines": { - "node": ">=12.22" + "node": ">=18" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "node_modules/@inquirer/rawlist": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.8.tgz", + "integrity": "sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.3.tgz", + "integrity": "sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.4.tgz", + "integrity": "sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -412,6 +1396,28 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -486,6 +1492,260 @@ "node": ">=14" } }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stryker-mutator/api": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-9.1.1.tgz", + "integrity": "sha512-rcN3GDz8MusRVdyRA4n3Z90/aVb3xbhaBK0hIcD+d62o6U47l/grGFA3bLAVM++cyCAoRYu6UkaUxu3BeOZnOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-metrics": "3.5.1", + "mutation-testing-report-schema": "3.5.1", + "tslib": "~2.8.0", + "typed-inject": "~5.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@stryker-mutator/api/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-9.1.1.tgz", + "integrity": "sha512-KB+J+J/lHh8zbLPdGOSgcpBA/X1Di2vJt0HCdMdIGJgdTITTb0+b2J7NNfzchnUIsi24rm+whPVZRiah8M/stg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@inquirer/prompts": "^7.0.0", + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/instrumenter": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "ajv": "~8.17.1", + "chalk": "~5.4.0", + "commander": "~14.0.0", + "diff-match-patch": "1.0.5", + "emoji-regex": "~10.4.0", + "execa": "~9.6.0", + "file-url": "~4.0.0", + "json-rpc-2.0": "^1.7.0", + "lodash.groupby": "~4.6.0", + "minimatch": "~10.0.0", + "mutation-server-protocol": "~0.3.0", + "mutation-testing-elements": "3.5.3", + "mutation-testing-metrics": "3.5.1", + "mutation-testing-report-schema": "3.5.1", + "npm-run-path": "~6.0.0", + "progress": "~2.0.3", + "rxjs": "~7.8.1", + "semver": "^7.6.3", + "source-map": "~0.7.4", + "tree-kill": "~1.2.2", + "tslib": "2.8.1", + "typed-inject": "~5.0.0", + "typed-rest-client": "~2.1.0" + }, + "bin": { + "stryker": "bin/stryker.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@stryker-mutator/core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@stryker-mutator/core/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@stryker-mutator/core/node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@stryker-mutator/core/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stryker-mutator/core/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@stryker-mutator/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/core/node_modules/typed-rest-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", + "integrity": "sha512-Nel9aPbgSzRxfs1+4GoSB4wexCF+4Axlk7OSGVQCMa+4fWcyxIsN/YNmkp0xTT2iQzMD98h8yFLav/cNaULmRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "des.js": "^1.1.0", + "js-md4": "^0.3.2", + "qs": "^6.10.3", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/@stryker-mutator/instrumenter": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-9.1.1.tgz", + "integrity": "sha512-ykafMjVKdtweLCFUhcgilB0H6hm014yQo0lGjHpiyWIWG9xwMsI3JrrVQMj3jZRbd9ObfK2C0yWXiSy7uXvrtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "~7.28.0", + "@babel/generator": "~7.28.0", + "@babel/parser": "~7.28.0", + "@babel/plugin-proposal-decorators": "~7.28.0", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/preset-typescript": "~7.27.0", + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "angular-html-parser": "~9.2.0", + "semver": "~7.7.0", + "weapon-regex": "~1.3.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@stryker-mutator/mocha-runner": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/mocha-runner/-/mocha-runner-9.1.1.tgz", + "integrity": "sha512-ASq5zsFlb75xIhHRQYBYbmgUmf+THJDp38pEjCB3dhHZuev2d8nvI+9UO+Cc48hWDhp5D2uNMcYC105QE8sF4g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "tslib": "~2.8.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@stryker-mutator/core": "~9.1.0", + "mocha": ">= 7.2 < 12" + } + }, + "node_modules/@stryker-mutator/mocha-runner/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@stryker-mutator/typescript-checker": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/typescript-checker/-/typescript-checker-9.1.1.tgz", + "integrity": "sha512-SYXAiOO/2pME6Dq34RbLuYv3yf3GU35hIpjicEcVebiF+RsyFOrG91SCX1hSaE/Q5AWhaFBvwGDJ3VwZaGjDAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "semver": "~7.7.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@stryker-mutator/core": "~9.1.0", + "typescript": ">=3.6" + } + }, + "node_modules/@stryker-mutator/util": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-9.1.1.tgz", + "integrity": "sha512-F2LR61gWgxBj0dUnkmGS0XydIapIRu+ii2X7Tt+FrcyfQrpykCvV3TZqQJ2aRkg8VFn5dkjtL9cQKEepWHoBFg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@types/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", @@ -1360,6 +2620,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/angular-html-parser": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-9.2.0.tgz", + "integrity": "sha512-jfnGrA5hguEcvHPrHUsrWOs8jk6SE9cQzFHxt3FPGwzvSEBXLAawReXylh492rzz5km5VgR664EUDMNnmYstSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1527,6 +2797,16 @@ ], "optional": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1587,6 +2867,40 @@ "dev": true, "license": "ISC" }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1689,6 +3003,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1705,6 +3040,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "dev": true, + "license": "MIT" + }, "node_modules/cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -1795,6 +3137,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1927,10 +3279,11 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2067,6 +3420,17 @@ "node": ">=0.4.0" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -2087,6 +3451,13 @@ "node": ">=0.3.1" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2183,6 +3554,13 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2660,6 +4038,46 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -2716,6 +4134,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -2730,8 +4165,37 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "dependencies": { - "pend": "~1.2.0" + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/file-entry-cache": { @@ -2746,6 +4210,19 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-4.0.0.tgz", + "integrity": "sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2905,6 +4382,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2942,6 +4429,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -3229,6 +4733,33 @@ "node": ">= 14" } }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3565,6 +5096,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -3720,6 +5264,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-sdsl": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", @@ -3730,6 +5281,13 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3742,6 +5300,26 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-rpc-2.0": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.7.1.tgz", + "integrity": "sha512-JqZjhjAanbpkXIzFE7u8mE/iFblawwlXtONaCvRqI+pyABVz7B4M1EUNpyVW+dZjqgQ2L5HFmZCmOCgUKm00hg==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3946,6 +5524,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4129,6 +5714,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4313,6 +5905,43 @@ "dev": true, "license": "MIT" }, + "node_modules/mutation-server-protocol": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mutation-server-protocol/-/mutation-server-protocol-0.3.0.tgz", + "integrity": "sha512-pQY+lb80vuD33P1NwhDyCWUgwP2w6JAP5+9Hz3CWM2HpIoYxDkT7OXYKabaunKnoSCgutP3MuruzPCXxLX/lnQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mutation-testing-elements": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-3.5.3.tgz", + "integrity": "sha512-Vr76a77/mFGsiSAUL+1xFEDb3n5lFs7UJKGWHtaJ+C85kutpBU3QVQ88zobo8Y0dNZPgcMrfThjOzp7W4nmLlQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mutation-testing-metrics": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-3.5.1.tgz", + "integrity": "sha512-mNgEcnhyBDckgoKg1kjG/4Uo3aBCW0WdVUxINVEazMTggPtqGfxaAlQ9GjItyudu/8S9DuspY3xUaIRLozFG9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mutation-testing-report-schema": "3.5.1" + } + }, + "node_modules/mutation-testing-report-schema": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-3.5.1.tgz", + "integrity": "sha512-tu5ATRxGH3sf2igiTKonxlCsWnWcD3CYr3IXGUym7yTh3Mj5NoJsu7bDkJY99uOrEp6hQByC2nRUPEGfe6EnAg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -4358,6 +5987,13 @@ "dev": true, "optional": true }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4368,6 +6004,36 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4697,6 +6363,19 @@ "node": ">=6" } }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -4870,6 +6549,22 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4877,6 +6572,16 @@ "dev": true, "license": "MIT" }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5046,6 +6751,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -5137,6 +6852,23 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5171,6 +6903,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -5178,9 +6917,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -5311,6 +7050,16 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -5495,6 +7244,19 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5618,6 +7380,16 @@ "node": ">=8.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -5711,6 +7483,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-inject": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-5.0.0.tgz", + "integrity": "sha512-0Ql2ORqBORLMdAW89TQKZsb1PQkFGImFfVmncXWe7a+AA3+7dh7Se9exxZowH4kbnlvKEFkMxUYdHUpjYWFJaA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/typed-rest-client": { "version": "1.8.9", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", @@ -5763,6 +7545,50 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5809,6 +7635,13 @@ "node": ">=10.12.0" } }, + "node_modules/weapon-regex": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-1.3.6.tgz", + "integrity": "sha512-wsf1m1jmMrso5nhwVFJJHSubEBf3+pereGd7+nBKtYJ18KoB/PWJOHS3WRkwS04VrOU0iJr2bZU+l1QaTJ+9nA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6094,17 +7927,53 @@ "buffer-crc32": "~0.2.3" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -6299,6 +8168,381 @@ "uuid": "^8.3.0" } }, + "@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + } + }, + "@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true + }, + "@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "requires": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "requires": { + "@babel/types": "^7.27.3" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "requires": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + } + }, + "@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "requires": { + "@babel/types": "^7.27.1" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + } + }, + "@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true + }, + "@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "requires": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + } + }, + "@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "requires": { + "@babel/types": "^7.28.4" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + } + }, + "@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + } + }, + "@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + } + }, + "@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + } + }, + "@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + } + }, + "@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + } + }, "@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -6360,12 +8604,238 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@inquirer/ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz", + "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==", + "dev": true + }, + "@inquirer/checkbox": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.4.tgz", + "integrity": "sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/confirm": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.18.tgz", + "integrity": "sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz", + "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "@inquirer/editor": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz", + "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/external-editor": "^1.0.2", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/expand": { + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.20.tgz", + "integrity": "sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/external-editor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", + "dev": true, + "requires": { + "chardet": "^2.1.0", + "iconv-lite": "^0.7.0" + } + }, + "@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "dev": true + }, + "@inquirer/input": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.4.tgz", + "integrity": "sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/number": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.20.tgz", + "integrity": "sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/password": { + "version": "4.0.20", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.20.tgz", + "integrity": "sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/prompts": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.6.tgz", + "integrity": "sha512-68JhkiojicX9SBUD8FE/pSKbOKtwoyaVj1kwqLfvjlVXZvOy3iaSWX4dCLsZyYx/5Ur07Fq+yuDNOen+5ce6ig==", + "dev": true, + "requires": { + "@inquirer/checkbox": "^4.2.4", + "@inquirer/confirm": "^5.1.18", + "@inquirer/editor": "^4.2.20", + "@inquirer/expand": "^4.0.20", + "@inquirer/input": "^4.2.4", + "@inquirer/number": "^3.0.20", + "@inquirer/password": "^4.0.20", + "@inquirer/rawlist": "^4.1.8", + "@inquirer/search": "^3.1.3", + "@inquirer/select": "^4.3.4" + } + }, + "@inquirer/rawlist": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.8.tgz", + "integrity": "sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/search": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.3.tgz", + "integrity": "sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==", + "dev": true, + "requires": { + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/select": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.4.tgz", + "integrity": "sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==", + "dev": true, + "requires": { + "@inquirer/ansi": "^1.0.0", + "@inquirer/core": "^10.2.2", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "dev": true, + "requires": {} + }, + "@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "dev": true }, + "@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "requires": { + "@isaacs/balanced-match": "^4.0.1" + } + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -6403,6 +8873,26 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -6458,6 +8948,194 @@ "dev": true, "optional": true }, + "@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true + }, + "@stryker-mutator/api": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-9.1.1.tgz", + "integrity": "sha512-rcN3GDz8MusRVdyRA4n3Z90/aVb3xbhaBK0hIcD+d62o6U47l/grGFA3bLAVM++cyCAoRYu6UkaUxu3BeOZnOg==", + "dev": true, + "requires": { + "mutation-testing-metrics": "3.5.1", + "mutation-testing-report-schema": "3.5.1", + "tslib": "~2.8.0", + "typed-inject": "~5.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@stryker-mutator/core": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-9.1.1.tgz", + "integrity": "sha512-KB+J+J/lHh8zbLPdGOSgcpBA/X1Di2vJt0HCdMdIGJgdTITTb0+b2J7NNfzchnUIsi24rm+whPVZRiah8M/stg==", + "dev": true, + "requires": { + "@inquirer/prompts": "^7.0.0", + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/instrumenter": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "ajv": "~8.17.1", + "chalk": "~5.4.0", + "commander": "~14.0.0", + "diff-match-patch": "1.0.5", + "emoji-regex": "~10.4.0", + "execa": "~9.6.0", + "file-url": "~4.0.0", + "json-rpc-2.0": "^1.7.0", + "lodash.groupby": "~4.6.0", + "minimatch": "~10.0.0", + "mutation-server-protocol": "~0.3.0", + "mutation-testing-elements": "3.5.3", + "mutation-testing-metrics": "3.5.1", + "mutation-testing-report-schema": "3.5.1", + "npm-run-path": "~6.0.0", + "progress": "~2.0.3", + "rxjs": "~7.8.1", + "semver": "^7.6.3", + "source-map": "~0.7.4", + "tree-kill": "~1.2.2", + "tslib": "2.8.1", + "typed-inject": "~5.0.0", + "typed-rest-client": "~2.1.0" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true + }, + "commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "dev": true + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "requires": { + "@isaacs/brace-expansion": "^5.0.0" + } + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "typed-rest-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", + "integrity": "sha512-Nel9aPbgSzRxfs1+4GoSB4wexCF+4Axlk7OSGVQCMa+4fWcyxIsN/YNmkp0xTT2iQzMD98h8yFLav/cNaULmRA==", + "dev": true, + "requires": { + "des.js": "^1.1.0", + "js-md4": "^0.3.2", + "qs": "^6.10.3", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + } + } + }, + "@stryker-mutator/instrumenter": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-9.1.1.tgz", + "integrity": "sha512-ykafMjVKdtweLCFUhcgilB0H6hm014yQo0lGjHpiyWIWG9xwMsI3JrrVQMj3jZRbd9ObfK2C0yWXiSy7uXvrtg==", + "dev": true, + "requires": { + "@babel/core": "~7.28.0", + "@babel/generator": "~7.28.0", + "@babel/parser": "~7.28.0", + "@babel/plugin-proposal-decorators": "~7.28.0", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/preset-typescript": "~7.27.0", + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "angular-html-parser": "~9.2.0", + "semver": "~7.7.0", + "weapon-regex": "~1.3.2" + } + }, + "@stryker-mutator/mocha-runner": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/mocha-runner/-/mocha-runner-9.1.1.tgz", + "integrity": "sha512-ASq5zsFlb75xIhHRQYBYbmgUmf+THJDp38pEjCB3dhHZuev2d8nvI+9UO+Cc48hWDhp5D2uNMcYC105QE8sF4g==", + "dev": true, + "requires": { + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "tslib": "~2.8.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, + "@stryker-mutator/typescript-checker": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/typescript-checker/-/typescript-checker-9.1.1.tgz", + "integrity": "sha512-SYXAiOO/2pME6Dq34RbLuYv3yf3GU35hIpjicEcVebiF+RsyFOrG91SCX1hSaE/Q5AWhaFBvwGDJ3VwZaGjDAw==", + "dev": true, + "requires": { + "@stryker-mutator/api": "9.1.1", + "@stryker-mutator/util": "9.1.1", + "semver": "~7.7.0" + } + }, + "@stryker-mutator/util": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-9.1.1.tgz", + "integrity": "sha512-F2LR61gWgxBj0dUnkmGS0XydIapIRu+ii2X7Tt+FrcyfQrpykCvV3TZqQJ2aRkg8VFn5dkjtL9cQKEepWHoBFg==", + "dev": true + }, "@types/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", @@ -7044,6 +9722,12 @@ "uri-js": "^4.2.2" } }, + "angular-html-parser": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/angular-html-parser/-/angular-html-parser-9.2.0.tgz", + "integrity": "sha512-jfnGrA5hguEcvHPrHUsrWOs8jk6SE9cQzFHxt3FPGwzvSEBXLAawReXylh492rzz5km5VgR664EUDMNnmYstSQ==", + "dev": true + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -7153,6 +9837,12 @@ "dev": true, "optional": true }, + "baseline-browser-mapping": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", + "dev": true + }, "binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -7202,6 +9892,19 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "requires": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -7266,6 +9969,12 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7276,6 +9985,12 @@ "supports-color": "^7.1.0" } }, + "chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "dev": true + }, "cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", @@ -7336,6 +10051,12 @@ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true }, + "cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true + }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -7438,9 +10159,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -7527,6 +10248,16 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -7540,6 +10271,12 @@ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7610,6 +10347,12 @@ "safe-buffer": "^5.0.1" } }, + "electron-to-chromium": { + "version": "1.5.228", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", + "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", + "dev": true + }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -7983,6 +10726,34 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, + "execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "requires": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + } + } + }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -8032,6 +10803,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -8050,6 +10827,23 @@ "pend": "~1.2.0" } }, + "figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "requires": { + "is-unicode-supported": "^2.0.0" + }, + "dependencies": { + "is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true + } + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8059,6 +10853,12 @@ "flat-cache": "^3.0.4" } }, + "file-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-4.0.0.tgz", + "integrity": "sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==", + "dev": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -8174,6 +10974,12 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -8197,6 +11003,16 @@ "has-symbols": "^1.0.3" } }, + "get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "requires": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + } + }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -8394,6 +11210,21 @@ "debug": "4" } }, + "human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -8611,6 +11442,12 @@ "call-bind": "^1.0.2" } }, + "is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -8714,12 +11551,24 @@ "@isaacs/cliui": "^8.0.2" } }, + "js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true + }, "js-sdsl": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", "dev": true }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -8729,6 +11578,18 @@ "argparse": "^2.0.1" } }, + "jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true + }, + "json-rpc-2.0": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.7.1.tgz", + "integrity": "sha512-JqZjhjAanbpkXIzFE7u8mE/iFblawwlXtONaCvRqI+pyABVz7B4M1EUNpyVW+dZjqgQ2L5HFmZCmOCgUKm00hg==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8907,6 +11768,12 @@ "p-locate": "^5.0.0" } }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -9033,6 +11900,12 @@ "dev": true, "optional": true }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9164,6 +12037,36 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "mutation-server-protocol": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mutation-server-protocol/-/mutation-server-protocol-0.3.0.tgz", + "integrity": "sha512-pQY+lb80vuD33P1NwhDyCWUgwP2w6JAP5+9Hz3CWM2HpIoYxDkT7OXYKabaunKnoSCgutP3MuruzPCXxLX/lnQ==", + "dev": true, + "requires": { + "zod": "^3.23.8" + } + }, + "mutation-testing-elements": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-3.5.3.tgz", + "integrity": "sha512-Vr76a77/mFGsiSAUL+1xFEDb3n5lFs7UJKGWHtaJ+C85kutpBU3QVQ88zobo8Y0dNZPgcMrfThjOzp7W4nmLlQ==", + "dev": true + }, + "mutation-testing-metrics": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-3.5.1.tgz", + "integrity": "sha512-mNgEcnhyBDckgoKg1kjG/4Uo3aBCW0WdVUxINVEazMTggPtqGfxaAlQ9GjItyudu/8S9DuspY3xUaIRLozFG9g==", + "dev": true, + "requires": { + "mutation-testing-report-schema": "3.5.1" + } + }, + "mutation-testing-report-schema": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-3.5.1.tgz", + "integrity": "sha512-tu5ATRxGH3sf2igiTKonxlCsWnWcD3CYr3IXGUym7yTh3Mj5NoJsu7bDkJY99uOrEp6hQByC2nRUPEGfe6EnAg==", + "dev": true + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -9206,12 +12109,36 @@ "dev": true, "optional": true }, + "node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "requires": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "dependencies": { + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + } + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -9430,6 +12357,12 @@ "callsites": "^3.0.0" } }, + "parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true + }, "parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -9559,12 +12492,27 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "requires": { + "parse-ms": "^4.0.0" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -9678,6 +12626,12 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -9729,6 +12683,23 @@ "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9746,6 +12717,12 @@ "is-regex": "^1.1.4" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -9753,9 +12730,9 @@ "dev": true }, "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true }, "serialize-javascript": { @@ -9830,6 +12807,12 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true + }, "stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -9956,6 +12939,12 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10042,6 +13031,12 @@ "is-number": "^7.0.0" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -10111,6 +13106,12 @@ "is-typed-array": "^1.1.9" } }, + "typed-inject": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/typed-inject/-/typed-inject-5.0.0.tgz", + "integrity": "sha512-0Ql2ORqBORLMdAW89TQKZsb1PQkFGImFfVmncXWe7a+AA3+7dh7Se9exxZowH4kbnlvKEFkMxUYdHUpjYWFJaA==", + "dev": true + }, "typed-rest-client": { "version": "1.8.9", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", @@ -10152,6 +13153,22 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, + "unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "requires": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10190,6 +13207,12 @@ "convert-source-map": "^2.0.0" } }, + "weapon-regex": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-1.3.6.tgz", + "integrity": "sha512-wsf1m1jmMrso5nhwVFJJHSubEBf3+pereGd7+nBKtYJ18KoB/PWJOHS3WRkwS04VrOU0iJr2bZU+l1QaTJ+9nA==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10402,6 +13425,24 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true + }, + "yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true + }, + "zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true } } } diff --git a/package.json b/package.json index 49a5445..53f972d 100644 --- a/package.json +++ b/package.json @@ -73,9 +73,14 @@ "watch": "tsc --watch", "build": "tsc", "pretest": "npm run build", - "test": "vscode-test" + "test": "vscode-test", + "test:unit": "mocha --config .mocharc.unit.json", + "test:mutation": "stryker run" }, "devDependencies": { + "@stryker-mutator/core": "^9.1.1", + "@stryker-mutator/mocha-runner": "^9.1.1", + "@stryker-mutator/typescript-checker": "^9.1.1", "@types/glob": "^8.1.0", "@types/mocha": "^10.0.10", "@types/node": "^20", diff --git a/src/core/docblockGenerator.ts b/src/core/docblockGenerator.ts new file mode 100644 index 0000000..bcd66f9 --- /dev/null +++ b/src/core/docblockGenerator.ts @@ -0,0 +1,42 @@ +import { Tag } from '../types/index.js'; + +export function generateDocblockLines( + description: string, + params: Tag[], + returnParam?: Tag, +): string[] { + const docblockLines = [ + '/**', + ` * ${description}`, + ' *', + ]; + + const paramTypeLengths: number[] = [0]; + const paramNameLengths: number[] = [0]; + + params.forEach((param) => { + if (param.types) { + paramTypeLengths.push(param.types.join('|').length); + } + if (param.variable) { + paramNameLengths.push(param.variable.length); + } + }); + + const longestParamType = Math.max(...paramTypeLengths); + const longestParamName = Math.max(...paramNameLengths); + + params.forEach((param) => { + const types = param.types?.join('|').padEnd(longestParamType, ' ') || ''; + const variable = param.variable?.padEnd(longestParamName, ' ') || ''; + docblockLines.push(` * @param ${types} ${variable} ${param.content}`); + }); + + if (returnParam) { + docblockLines.push(` * @return ${returnParam.types?.join('|') || ''} ${returnParam.content}`); + } + + docblockLines.push(' */'); + + return docblockLines; +} diff --git a/src/core/hookHelpers.ts b/src/core/hookHelpers.ts new file mode 100644 index 0000000..5a866ec --- /dev/null +++ b/src/core/hookHelpers.ts @@ -0,0 +1,101 @@ +import { hooks as actions } from '@wp-hooks/wordpress-core/hooks/actions.json'; +import { hooks as filters } from '@wp-hooks/wordpress-core/hooks/filters.json'; +import { Hook, Tag } from '../types'; + +export function getHook( + name: string, +): Hook | void { + let hooks = filters.filter((filter) => { + if (filter.name === name) { + return true; + } + + if (filter.aliases?.includes(name)) { + return true; + } + + return false; + }); + + if (hooks.length === 0) { + hooks = actions.filter((action) => { + if (action.name === name) { + return true; + } + + if (action.aliases?.includes(name)) { + return true; + } + + return false; + }); + } + + if (hooks.length) { + return hooks[0]; + } +} + +export function getHookSlug( + hook: Hook, +): string { + return hook.name.toLowerCase().replace(/[^a-z_-]/g, ''); +} + +export interface HookDescriptionData { + description: string; + slug: string; + params: Tag[]; + otherTags: Tag[]; +} + +export function getHookDescriptionData( + hook: Hook, +): HookDescriptionData { + let description = hook.doc.long_description; + const slug = getHookSlug(hook); + + description += `\n\n[View on developer.wordpress.org →](https://developer.wordpress.org/reference/hooks/${slug}/)\n\n`; + + const params = hook.doc.tags.filter((tag) => tag.name === 'param'); + + params.forEach((tag: Tag) => { + if (!tag.types) { + return; + } + + const types = tag.types.join('|'); + description += `\n\n_@param_ \`${types} ${tag.variable}\` \n${tag.content}`; + }); + + const everythingElse = hook.doc.tags.filter((tag) => tag.name !== 'param'); + + everythingElse.forEach((tag: Tag) => { + description += `\n\n_@${tag.name}_ ${tag.content || tag.refers || ''} ${tag.description || ''}`; + }); + + return { + description, + slug, + params, + otherTags: everythingElse, + }; +} + +export interface HookCompletionData { + name: string; + detail: string; + descriptionData: HookDescriptionData; + filterText?: string; +} + +export function getHookCompletionData( + hook: Hook, +): HookCompletionData { + return { + name: hook.name, + detail: hook.doc.description, + descriptionData: getHookDescriptionData(hook), + filterText: hook.aliases?.join(' '), + }; +} diff --git a/src/core/matchers.ts b/src/core/matchers.ts new file mode 100644 index 0000000..cf73b75 --- /dev/null +++ b/src/core/matchers.ts @@ -0,0 +1,18 @@ +export function isInFilter( + line: string, +): RegExpMatchArray | null { + return line.match(/(add|remove|has|doing)_filter\([\s]*('|")[^"|']*$/); +} + +export function isInAction( + line: string, +): RegExpMatchArray | null { + return line.match(/(add|remove|has|doing|did)_action\([\s]*('|")[^"|']*$/); +} + +export function isInFunctionDeclaration( + line: string, +): RegExpMatchArray | null { + // add_ filter|action ( '" {hook} '" , + return line.match(/add_(?:filter|action)\(\s*['"](?\S+?)['"],\s*\w*?$/); +} diff --git a/src/core/snippetGenerator.ts b/src/core/snippetGenerator.ts new file mode 100644 index 0000000..94d82c5 --- /dev/null +++ b/src/core/snippetGenerator.ts @@ -0,0 +1,83 @@ +import { Tag } from '../types/index.js'; +import { getTagType, getReturnType } from './typeHelpers.js'; + +export interface CallbackSnippet { + snippetCallback: string; + documentationCallback: string; + returnTypeString: string; + suffix: string; +} + +export function generateCallbackSnippet( + hookType: string, + params: Tag[], +): CallbackSnippet { + const snippetArgsString = params.map((param) => { + const val = `\\${param.variable}`; + const type = getTagType(param); + + if (!type) { + return val; + } + + if (!type.nullable) { + return `${type.type} ${val}`; + } + + return `?${type.type} ${val}`; + }).join(', '); + + const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); + const suffix = (params.length > 1 ? `, 10, ${params.length} ` : ' '); + + let snippetCallback = ''; + let documentationCallback = ''; + let returnTypeString = ''; + + if (hookType === 'filter' || hookType === 'filter_reference') { + const returnType = getReturnType(params[0]); + + if (returnType) { + if (returnType.nullable) { + returnTypeString = ` : ?${returnType.type}`; + } else { + returnTypeString = ` : ${returnType.type}`; + } + } + + snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; + documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; + } else { + const actionArgsString = snippetArgsString ? ` ${snippetArgsString} ` : ''; + snippetCallback = `(${actionArgsString}) : void {\n\t\${1}\n}`; + documentationCallback = `(${docArgsString}) : void {\n}`; + } + + return { + snippetCallback, + documentationCallback, + returnTypeString, + suffix, + }; +} + +export const WORDPRESS_UTILITY_SNIPPETS = { + __return_true: 'Return true', + __return_false: 'Return false', + __return_zero: 'Return zero', + __return_empty_array: 'Return empty array', + __return_empty_string: 'Return empty string', +}; + +export const SNIPPET_TYPES: { [key: string]: string[] } = { + null: [], + self: [], + array: ['__return_empty_array'], + callable: [], + bool: ['__return_true', '__return_false'], + float: ['__return_zero'], + int: ['__return_zero'], + string: ['__return_empty_string'], + iterable: ['__return_empty_array'], + object: [], +}; diff --git a/src/core/typeHelpers.ts b/src/core/typeHelpers.ts new file mode 100644 index 0000000..bc1d418 --- /dev/null +++ b/src/core/typeHelpers.ts @@ -0,0 +1,93 @@ +import { Tag, TagType } from '../types'; + +export function getTagType( + tag: Tag, +): TagType | null { + // https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration + const allowedTypes: { [key: string]: number } = { + self: 5.0, + array: 5.1, + callable: 5.4, + bool: 7.0, + float: 7.0, + int: 7.0, + string: 7.0, + iterable: 7.1, + object: 7.2, + }; + + const typeData: TagType = { + type: '', + nullable: false, + }; + + // No type info? Bail. + if (!tag.types) { + return null; + } + + const types = [...tag.types]; + + // Handle nullable type. + if (types.length === 2) { + if (types[0] === 'null') { + types.splice(0, 1); + typeData.nullable = true; + } else if (types[1] === 'null') { + types.splice(1, 1); + typeData.nullable = true; + } + } + + // More than one type? Bail. + if (types.length !== 1) { + return null; + } + + let type = types[0]; + + // Un-hintable type? Bail. + if (['mixed'].includes(type)) { + return null; + } + + // Hinting for typed-arrays. + if (type.indexOf('[]') !== -1) { + type = 'array'; + } + + // Aliases for bool. + if (['false', 'true', 'boolean'].includes(type)) { + type = 'bool'; + } + + // Alias for callable. + if (type === 'callback') { + type = 'callable'; + } + + // Alias for int. + if (type === 'integer') { + type = 'int'; + } + + // Convert stdClass to object to avoid fatals when the stdClass gets promoted to a real class. + if (type === '\\stdClass') { + type = 'object'; + } + + // Check the allowed types, ignoring unknown types such as class and interface names. + if (allowedTypes[type]) { + return null; + } + + typeData.type = type; + + return typeData; +} + +export function getReturnType( + tag: Tag, +) : TagType | null { + return getTagType(tag); +} diff --git a/src/generators/docblockGenerator.ts b/src/generators/docblockGenerator.ts index bcd66f9..a295d92 100644 --- a/src/generators/docblockGenerator.ts +++ b/src/generators/docblockGenerator.ts @@ -1,42 +1,4 @@ -import { Tag } from '../types/index.js'; - -export function generateDocblockLines( - description: string, - params: Tag[], - returnParam?: Tag, -): string[] { - const docblockLines = [ - '/**', - ` * ${description}`, - ' *', - ]; - - const paramTypeLengths: number[] = [0]; - const paramNameLengths: number[] = [0]; - - params.forEach((param) => { - if (param.types) { - paramTypeLengths.push(param.types.join('|').length); - } - if (param.variable) { - paramNameLengths.push(param.variable.length); - } - }); - - const longestParamType = Math.max(...paramTypeLengths); - const longestParamName = Math.max(...paramNameLengths); - - params.forEach((param) => { - const types = param.types?.join('|').padEnd(longestParamType, ' ') || ''; - const variable = param.variable?.padEnd(longestParamName, ' ') || ''; - docblockLines.push(` * @param ${types} ${variable} ${param.content}`); - }); - - if (returnParam) { - docblockLines.push(` * @return ${returnParam.types?.join('|') || ''} ${returnParam.content}`); - } - - docblockLines.push(' */'); - - return docblockLines; -} +// Re-export from core for backward compatibility +export { + generateDocblockLines, +} from '../core/docblockGenerator.js'; diff --git a/src/generators/snippetGenerator.ts b/src/generators/snippetGenerator.ts index 9225313..4a99de8 100644 --- a/src/generators/snippetGenerator.ts +++ b/src/generators/snippetGenerator.ts @@ -1,83 +1,7 @@ -import { Tag } from '../types/index.js'; -import { getTagType, getReturnType } from '../utils/typeHelpers.js'; - -export interface CallbackSnippet { - snippetCallback: string; - documentationCallback: string; - returnTypeString: string; - suffix: string; -} - -export function generateCallbackSnippet( - hookType: string, - params: Tag[], -): CallbackSnippet { - const snippetArgsString = params.map((param) => { - const val = `\\${param.variable}`; - const type = getTagType(param); - - if (!type) { - return val; - } - - if (!type.nullable) { - return `${type.type} ${val}`; - } - - return `?${type.type} ${val}`; - }).join(', '); - - const docArgsString = snippetArgsString.replace(/\\\$/g, '$'); - const suffix = (params.length > 1 ? `, 10, ${params.length} ` : ' '); - - let snippetCallback = ''; - let documentationCallback = ''; - let returnTypeString = ''; - - if (hookType === 'filter' || hookType === 'filter_reference') { - const returnType = getReturnType(params[0]); - - if (returnType) { - if (returnType.nullable) { - returnTypeString = ` : ?${returnType.type}`; - } else { - returnTypeString = ` : ${returnType.type}`; - } - } - - snippetCallback = `( ${snippetArgsString} )${returnTypeString} {\n\t\${1}\n\treturn \\${params[0].variable};\n}`; - documentationCallback = `( ${docArgsString} )${returnTypeString} {\n\treturn ${params[0].variable};\n}`; - } else { - const actionArgsString = snippetArgsString ? ` ${snippetArgsString} ` : ''; - snippetCallback = `(${actionArgsString}) : void {\n\t\${1}\n}`; - documentationCallback = `(${docArgsString}) : void {\n}`; - } - - return { - snippetCallback, - documentationCallback, - returnTypeString, - suffix, - }; -} - -export const WORDPRESS_UTILITY_SNIPPETS = { - __return_true: 'Return true', - __return_false: 'Return false', - __return_zero: 'Return zero', - __return_empty_array: 'Return empty array', - __return_empty_string: 'Return empty string', -}; - -export const SNIPPET_TYPES: { [key: string]: string[] } = { - null: [], - self: [], - array: ['__return_empty_array'], - callable: [], - bool: ['__return_true', '__return_false'], - float: ['__return_zero'], - int: ['__return_zero'], - string: ['__return_empty_string'], - iterable: ['__return_empty_array'], - object: [], -}; +// Re-export from core for backward compatibility +export type { CallbackSnippet } from '../core/snippetGenerator.js'; +export { + generateCallbackSnippet, + WORDPRESS_UTILITY_SNIPPETS, + SNIPPET_TYPES, +} from '../core/snippetGenerator.js'; diff --git a/src/test/unit/core-docblockGenerator.test.ts b/src/test/unit/core-docblockGenerator.test.ts new file mode 100644 index 0000000..c383478 --- /dev/null +++ b/src/test/unit/core-docblockGenerator.test.ts @@ -0,0 +1,110 @@ +import * as assert from 'assert'; +import { generateDocblockLines } from '../../core/docblockGenerator.js'; +import { Tag } from '../../types/index.js'; + +suite('Core Docblock Generator Test Suite', () => { + suite('generateDocblockLines', () => { + test('Should generate basic docblock with description only', () => { + const result = generateDocblockLines('Test description', []); + assert.strictEqual(result[0], '/**'); + assert.strictEqual(result[1], ' * Test description'); + assert.strictEqual(result[2], ' *'); + assert.strictEqual(result[3], ' */'); + assert.strictEqual(result.length, 4); + }); + + test('Should include param tags', () => { + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$value', content: 'The value' }, + ]; + const result = generateDocblockLines('Test function', params); + assert.ok(result.some(line => line.includes('@param'))); + assert.ok(result.some(line => line.includes('string'))); + assert.ok(result.some(line => line.includes('$value'))); + }); + + test('Should include multiple param tags', () => { + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$arg1', content: 'First arg' }, + { name: 'param', types: ['int'], variable: '$arg2', content: 'Second arg' }, + ]; + const result = generateDocblockLines('Test', params); + const paramLines = result.filter(line => line.includes('@param')); + assert.strictEqual(paramLines.length, 2); + }); + + test('Should handle union types in params', () => { + const params: Tag[] = [ + { name: 'param', types: ['string', 'int'], variable: '$value', content: 'Mixed value' }, + ]; + const result = generateDocblockLines('Test', params); + assert.ok(result.some(line => line.includes('string|int'))); + }); + + test('Should include return tag when provided', () => { + const returnParam: Tag = { name: 'return', types: ['bool'], content: 'Success status' }; + const result = generateDocblockLines('Test', [], returnParam); + assert.ok(result.some(line => line.includes('@return'))); + assert.ok(result.some(line => line.includes('bool'))); + }); + + test('Should handle params without types', () => { + const params: Tag[] = [ + { name: 'param', variable: '$value', content: 'The value' }, + ]; + const result = generateDocblockLines('Test', params); + assert.ok(result.some(line => line.includes('@param'))); + assert.ok(result.some(line => line.includes('$value'))); + }); + + test('Should handle params without variable names', () => { + const params: Tag[] = [ + { name: 'param', types: ['string'], content: 'A string value' }, + ]; + const result = generateDocblockLines('Test', params); + assert.ok(result.some(line => line.includes('@param'))); + }); + + test('Should generate complete docblock structure', () => { + const params: Tag[] = [ + { name: 'param', types: ['WP_Post'], variable: '$post', content: 'Post object' }, + ]; + const returnParam: Tag = { name: 'return', types: ['bool'], content: 'Success' }; + const result = generateDocblockLines('Process a post', params, returnParam); + + assert.strictEqual(result[0], '/**'); + assert.ok(result[1].includes('Process a post')); + assert.strictEqual(result[2], ' *'); + assert.ok(result.some(line => line.includes('@param'))); + assert.ok(result.some(line => line.includes('@return'))); + assert.strictEqual(result[result.length - 1], ' */'); + }); + + test('Should handle empty description', () => { + const result = generateDocblockLines('', []); + assert.strictEqual(result[1], ' * '); + }); + + test('Should close docblock with asterisk slash', () => { + const result = generateDocblockLines('Test', []); + assert.strictEqual(result[result.length - 1], ' */'); + }); + + test('Should handle return param without types', () => { + const returnParam: Tag = { name: 'return', content: 'A value' }; + const result = generateDocblockLines('Test', [], returnParam); + assert.ok(result.some(line => line.includes('@return'))); + assert.ok(result.some(line => line.includes('A value'))); + }); + + test('Should pad types to longest type length', () => { + const params: Tag[] = [ + { name: 'param', types: ['int'], variable: '$x', content: 'X' }, + { name: 'param', types: ['WP_Post'], variable: '$post', content: 'Post' }, + ]; + const result = generateDocblockLines('Test', params); + const paramLines = result.filter(line => line.includes('@param')); + assert.ok(paramLines.length === 2); + }); + }); +}); diff --git a/src/test/unit/core-hookHelpers.test.ts b/src/test/unit/core-hookHelpers.test.ts new file mode 100644 index 0000000..cd4bcac --- /dev/null +++ b/src/test/unit/core-hookHelpers.test.ts @@ -0,0 +1,140 @@ +import * as assert from 'assert'; +import { getHook, getHookSlug, getHookDescriptionData, getHookCompletionData } from '../../core/hookHelpers.js'; +import { Hook } from '../../types/index.js'; + +suite('Core Hook Helpers Test Suite', () => { + suite('getHook', () => { + test('Should find common action hooks', () => { + const hook = getHook('init'); + assert.ok(hook); + assert.strictEqual(hook.name, 'init'); + assert.strictEqual(hook.type, 'action'); + }); + + test('Should find common filter hooks', () => { + const hook = getHook('the_content'); + assert.ok(hook); + assert.strictEqual(hook.name, 'the_content'); + assert.strictEqual(hook.type, 'filter'); + }); + + test('Should return undefined for non-existent hooks', () => { + const hook = getHook('nonexistent_hook_name_12345'); + assert.strictEqual(hook, undefined); + }); + + test('Should check filters before actions', () => { + const filterHook = getHook('the_content'); + assert.ok(filterHook); + assert.strictEqual(filterHook.type, 'filter'); + }); + + test('Should fallback to actions if not found in filters', () => { + const hook = getHook('wp_footer'); + assert.ok(hook); + assert.strictEqual(hook.type, 'action'); + }); + }); + + suite('getHookSlug', () => { + test('Should convert hook name to slug', () => { + const hook: Hook = { name: 'the_content', type: 'filter' } as Hook; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'the_content'); + }); + + test('Should remove invalid characters', () => { + const hook: Hook = { name: 'hook{test}name', type: 'action' } as Hook; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'hooktestname'); + }); + + test('Should preserve underscores and hyphens', () => { + const hook: Hook = { name: 'my_hook-name', type: 'action' } as Hook; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'my_hook-name'); + }); + + test('Should convert to lowercase', () => { + const hook: Hook = { name: 'MyHookName', type: 'action' } as Hook; + const slug = getHookSlug(hook); + assert.strictEqual(slug, 'myhookname'); + }); + }); + + suite('getHookDescriptionData', () => { + test('Should return description data structure', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookDescriptionData(hook); + assert.ok(data.description); + assert.ok(data.slug); + assert.ok(Array.isArray(data.params)); + assert.ok(Array.isArray(data.otherTags)); + }); + + test('Should include long description', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookDescriptionData(hook); + assert.ok(data.description.includes(hook.doc.long_description)); + }); + + test('Should include developer.wordpress.org link', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookDescriptionData(hook); + assert.ok(data.description.includes('developer.wordpress.org')); + }); + + test('Should separate params from other tags', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookDescriptionData(hook); + const paramTags = hook.doc.tags.filter(tag => tag.name === 'param'); + const otherTags = hook.doc.tags.filter(tag => tag.name !== 'param'); + assert.strictEqual(data.params.length, paramTags.length); + assert.strictEqual(data.otherTags.length, otherTags.length); + }); + + test('Should include slug in data', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookDescriptionData(hook); + assert.strictEqual(data.slug, 'init'); + }); + }); + + suite('getHookCompletionData', () => { + test('Should return completion data structure', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookCompletionData(hook); + assert.strictEqual(data.name, hook.name); + assert.strictEqual(data.detail, hook.doc.description); + assert.ok(data.descriptionData); + }); + + test('Should include hook name', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookCompletionData(hook); + assert.strictEqual(data.name, 'init'); + }); + + test('Should include short description as detail', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookCompletionData(hook); + assert.ok(data.detail.length > 0); + }); + + test('Should include description data', () => { + const hook = getHook('init'); + assert.ok(hook); + const data = getHookCompletionData(hook); + assert.ok(data.descriptionData.description); + assert.ok(data.descriptionData.slug); + }); + }); +}); diff --git a/src/test/unit/core-matchers.test.ts b/src/test/unit/core-matchers.test.ts new file mode 100644 index 0000000..da30f67 --- /dev/null +++ b/src/test/unit/core-matchers.test.ts @@ -0,0 +1,100 @@ +import * as assert from 'assert'; +import { isInFilter, isInAction, isInFunctionDeclaration } from '../../core/matchers.js'; + +suite('Core Matchers Test Suite', () => { + suite('isInAction', () => { + test('Should match add_action with single quotes', () => { + const result = isInAction('add_action( \''); + assert.ok(result !== null); + }); + + test('Should match remove_action', () => { + const result = isInAction('remove_action( "'); + assert.ok(result !== null); + }); + + test('Should match has_action', () => { + const result = isInAction('has_action( \''); + assert.ok(result !== null); + }); + + test('Should match doing_action', () => { + const result = isInAction('doing_action( "'); + assert.ok(result !== null); + }); + + test('Should match did_action', () => { + const result = isInAction('did_action( \''); + assert.ok(result !== null); + }); + + test('Should not match filter functions', () => { + const result = isInAction('add_filter( \''); + assert.strictEqual(result, null); + }); + + test('Should not match incomplete syntax', () => { + const result = isInAction('add_action'); + assert.strictEqual(result, null); + }); + }); + + suite('isInFilter', () => { + test('Should match add_filter with single quotes', () => { + const result = isInFilter('add_filter( \''); + assert.ok(result !== null); + }); + + test('Should match remove_filter', () => { + const result = isInFilter('remove_filter( "'); + assert.ok(result !== null); + }); + + test('Should match has_filter', () => { + const result = isInFilter('has_filter( \''); + assert.ok(result !== null); + }); + + test('Should match doing_filter', () => { + const result = isInFilter('doing_filter( "'); + assert.ok(result !== null); + }); + + test('Should not match action functions', () => { + const result = isInFilter('add_action( \''); + assert.strictEqual(result, null); + }); + + test('Should not match incomplete syntax', () => { + const result = isInFilter('add_filter'); + assert.strictEqual(result, null); + }); + }); + + suite('isInFunctionDeclaration', () => { + test('Should match add_action with callback parameter', () => { + const result = isInFunctionDeclaration('add_action( \'init\', '); + assert.ok(result !== null); + }); + + test('Should match add_filter with callback parameter', () => { + const result = isInFunctionDeclaration('add_filter( "the_content", '); + assert.ok(result !== null); + }); + + test('Should capture hook name in groups', () => { + const result = isInFunctionDeclaration('add_action( \'init\', '); + assert.strictEqual(result?.groups?.hook, 'init'); + }); + + test('Should not match without callback parameter', () => { + const result = isInFunctionDeclaration('add_action( \'init\' '); + assert.strictEqual(result, null); + }); + + test('Should not match incomplete syntax', () => { + const result = isInFunctionDeclaration('add_action( \'init'); + assert.strictEqual(result, null); + }); + }); +}); diff --git a/src/test/unit/core-snippetGenerator.test.ts b/src/test/unit/core-snippetGenerator.test.ts new file mode 100644 index 0000000..09d4f43 --- /dev/null +++ b/src/test/unit/core-snippetGenerator.test.ts @@ -0,0 +1,99 @@ +import * as assert from 'assert'; +import { generateCallbackSnippet, WORDPRESS_UTILITY_SNIPPETS, SNIPPET_TYPES } from '../../core/snippetGenerator.js'; +import { Tag } from '../../types/index.js'; + +suite('Core Snippet Generator Test Suite', () => { + suite('generateCallbackSnippet', () => { + test('Should generate action callback with no parameters', () => { + const result = generateCallbackSnippet('action', []); + assert.strictEqual(result.snippetCallback, '() : void {\n\t${1}\n}'); + assert.strictEqual(result.suffix, ' '); + }); + + test('Should generate action callback with single parameter', () => { + const params: Tag[] = [{ name: 'param', types: ['string'], variable: '$value', content: '' }]; + const result = generateCallbackSnippet('action', params); + assert.ok(result.snippetCallback.includes('\\$value')); + assert.strictEqual(result.suffix, ' '); + }); + + test('Should generate action callback with multiple parameters', () => { + const params: Tag[] = [ + { name: 'param', types: ['string'], variable: '$arg1', content: '' }, + { name: 'param', types: ['int'], variable: '$arg2', content: '' }, + ]; + const result = generateCallbackSnippet('action', params); + assert.strictEqual(result.suffix, ', 10, 2 '); + }); + + test('Should generate filter callback with return statement', () => { + const params: Tag[] = [{ name: 'param', types: ['WP_Post'], variable: '$post', content: '' }]; + const result = generateCallbackSnippet('filter', params); + assert.ok(result.snippetCallback.includes('return \\$post')); + }); + + test('Should handle nullable return types for filters', () => { + const params: Tag[] = [{ name: 'param', types: ['null', 'WP_User'], variable: '$user', content: '' }]; + const result = generateCallbackSnippet('filter', params); + assert.strictEqual(result.returnTypeString, ' : ?WP_User'); + }); + + test('Should handle filter_reference hook type', () => { + const params: Tag[] = [{ name: 'param', types: ['array'], variable: '$data', content: '' }]; + const result = generateCallbackSnippet('filter_reference', params); + assert.ok(result.snippetCallback.includes('return \\$data')); + }); + + test('Should escape dollar signs in snippet', () => { + const params: Tag[] = [{ name: 'param', variable: '$test', content: '' }]; + const result = generateCallbackSnippet('action', params); + assert.ok(result.snippetCallback.includes('\\$test')); + assert.ok(result.documentationCallback.includes('$test')); + }); + + test('Should set correct suffix for multiple parameters', () => { + const params: Tag[] = [ + { name: 'param', variable: '$arg1', content: '' }, + { name: 'param', variable: '$arg2', content: '' }, + { name: 'param', variable: '$arg3', content: '' }, + ]; + const result = generateCallbackSnippet('action', params); + assert.strictEqual(result.suffix, ', 10, 3 '); + }); + + test('Should handle filter with no return type hint', () => { + const params: Tag[] = [{ name: 'param', types: ['string'], variable: '$value', content: '' }]; + const result = generateCallbackSnippet('filter', params); + assert.strictEqual(result.returnTypeString, ''); + }); + + test('Should handle nullable parameter types', () => { + const params: Tag[] = [{ name: 'param', types: ['null', 'WP_Error'], variable: '$error', content: '' }]; + const result = generateCallbackSnippet('action', params); + assert.ok(result.snippetCallback.includes('?WP_Error \\$error')); + }); + }); + + suite('WORDPRESS_UTILITY_SNIPPETS', () => { + test('Should contain expected utility functions', () => { + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_true, 'Return true'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_false, 'Return false'); + assert.strictEqual(WORDPRESS_UTILITY_SNIPPETS.__return_zero, 'Return zero'); + }); + }); + + suite('SNIPPET_TYPES', () => { + test('Should map bool to true/false snippets', () => { + assert.deepStrictEqual(SNIPPET_TYPES.bool, ['__return_true', '__return_false']); + }); + + test('Should map numeric types to zero snippet', () => { + assert.deepStrictEqual(SNIPPET_TYPES.float, ['__return_zero']); + assert.deepStrictEqual(SNIPPET_TYPES.int, ['__return_zero']); + }); + + test('Should map string to empty string snippet', () => { + assert.deepStrictEqual(SNIPPET_TYPES.string, ['__return_empty_string']); + }); + }); +}); diff --git a/src/test/unit/core-typeHelpers.test.ts b/src/test/unit/core-typeHelpers.test.ts new file mode 100644 index 0000000..70f35e9 --- /dev/null +++ b/src/test/unit/core-typeHelpers.test.ts @@ -0,0 +1,72 @@ +import * as assert from 'assert'; +import { getTagType, getReturnType } from '../../core/typeHelpers.js'; +import { Tag } from '../../types/index.js'; + +suite('Core Type Helpers Test Suite', () => { + suite('getTagType', () => { + test('Should return null when tag has no types', () => { + const tag: Tag = { name: 'param', variable: '$test', content: '' }; + const result = getTagType(tag); + assert.strictEqual(result, null); + }); + + test('Should return null for mixed type', () => { + const tag: Tag = { name: 'param', types: ['mixed'], variable: '$test', content: '' }; + const result = getTagType(tag); + assert.strictEqual(result, null); + }); + + test('Should return null for allowed PHP types', () => { + const allowedTypes = ['self', 'array', 'callable', 'bool', 'float', 'int', 'string', 'iterable', 'object']; + allowedTypes.forEach(type => { + const tag: Tag = { name: 'param', types: [type], variable: '$test', content: '' }; + const result = getTagType(tag); + assert.strictEqual(result, null, `Expected null for allowed type: ${type}`); + }); + }); + + test('Should handle nullable types with null first', () => { + const tag: Tag = { name: 'param', types: ['null', 'WP_Post'], variable: '$post', content: '' }; + const result = getTagType(tag); + assert.deepStrictEqual(result, { type: 'WP_Post', nullable: true }); + }); + + test('Should handle nullable types with null last', () => { + const tag: Tag = { name: 'param', types: ['WP_User', 'null'], variable: '$user', content: '' }; + const result = getTagType(tag); + assert.deepStrictEqual(result, { type: 'WP_User', nullable: true }); + }); + + test('Should return null for multiple non-nullable types', () => { + const tag: Tag = { name: 'param', types: ['string', 'int'], variable: '$value', content: '' }; + const result = getTagType(tag); + assert.strictEqual(result, null); + }); + + test('Should return class names as custom types', () => { + const tag: Tag = { name: 'param', types: ['WP_Post'], variable: '$post', content: '' }; + const result = getTagType(tag); + assert.deepStrictEqual(result, { type: 'WP_Post', nullable: false }); + }); + + test('Should handle namespaced class names', () => { + const tag: Tag = { name: 'param', types: ['\\Namespace\\ClassName'], variable: '$obj', content: '' }; + const result = getTagType(tag); + assert.deepStrictEqual(result, { type: '\\Namespace\\ClassName', nullable: false }); + }); + }); + + suite('getReturnType', () => { + test('Should delegate to getTagType', () => { + const tag: Tag = { name: 'return', types: ['WP_Post'], content: 'The post object' }; + const result = getReturnType(tag); + assert.deepStrictEqual(result, { type: 'WP_Post', nullable: false }); + }); + + test('Should return null for no types', () => { + const tag: Tag = { name: 'return', content: 'The result' }; + const result = getReturnType(tag); + assert.strictEqual(result, null); + }); + }); +}); diff --git a/src/utils/hookHelpers.ts b/src/utils/hookHelpers.ts index 25936bf..ab6b1d4 100644 --- a/src/utils/hookHelpers.ts +++ b/src/utils/hookHelpers.ts @@ -1,85 +1,32 @@ import * as vscode from 'vscode'; -import { hooks as actions } from '@wp-hooks/wordpress-core/hooks/actions.json'; -import { hooks as filters } from '@wp-hooks/wordpress-core/hooks/filters.json'; -import { Hook, Tag } from '../types'; +import { Hook } from '../types'; +import { + getHook as coreGetHook, + getHookSlug as coreGetHookSlug, + getHookDescriptionData, + getHookCompletionData, +} from '../core/hookHelpers.js'; -export function getHook( - name: string, -): Hook | void { - let hooks = filters.filter((filter) => { - if (filter.name === name) { - return true; - } - - if (filter.aliases?.includes(name)) { - return true; - } - - return false; - }); - - if (hooks.length === 0) { - hooks = actions.filter((action) => { - if (action.name === name) { - return true; - } - - if (action.aliases?.includes(name)) { - return true; - } - - return false; - }); - } - - if (hooks.length) { - return hooks[0]; - } -} - -export function getHookSlug( - hook: Hook, -): string { - return hook.name.toLowerCase().replace(/[^a-z_-]/g, ''); -} +// Re-export core functions for backward compatibility +export { coreGetHook as getHook, coreGetHookSlug as getHookSlug }; export function getHookDescription( hook: Hook, ): vscode.MarkdownString { - let description = hook.doc.long_description; - const slug = getHookSlug(hook); - - description += `\n\n[View on developer.wordpress.org →](https://developer.wordpress.org/reference/hooks/${slug}/)\n\n`; - - const params = hook.doc.tags.filter((tag) => tag.name === 'param'); - - params.forEach((tag: Tag) => { - if (!tag.types) { - return; - } - - const types = tag.types.join('|'); - description += `\n\n_@param_ \`${types} ${tag.variable}\` \n${tag.content}`; - }); - - const everythingElse = hook.doc.tags.filter((tag) => tag.name !== 'param'); - - everythingElse.forEach((tag: Tag) => { - description += `\n\n_@${tag.name}_ ${tag.content || tag.refers || ''} ${tag.description || ''}`; - }); - - return new vscode.MarkdownString(description); + const data = getHookDescriptionData(hook); + return new vscode.MarkdownString(data.description); } export function getHookCompletion( hook: Hook, ): vscode.CompletionItem { - const completion = new vscode.CompletionItem(hook.name, vscode.CompletionItemKind.Value); - completion.detail = hook.doc.description; - completion.documentation = getHookDescription(hook); + const data = getHookCompletionData(hook); + const completion = new vscode.CompletionItem(data.name, vscode.CompletionItemKind.Value); + completion.detail = data.detail; + completion.documentation = new vscode.MarkdownString(data.descriptionData.description); - if (hook.aliases) { - completion.filterText = hook.aliases.join(' '); + if (data.filterText) { + completion.filterText = data.filterText; } return completion; diff --git a/src/utils/matchers.ts b/src/utils/matchers.ts index cf73b75..d5c8804 100644 --- a/src/utils/matchers.ts +++ b/src/utils/matchers.ts @@ -1,18 +1,6 @@ -export function isInFilter( - line: string, -): RegExpMatchArray | null { - return line.match(/(add|remove|has|doing)_filter\([\s]*('|")[^"|']*$/); -} - -export function isInAction( - line: string, -): RegExpMatchArray | null { - return line.match(/(add|remove|has|doing|did)_action\([\s]*('|")[^"|']*$/); -} - -export function isInFunctionDeclaration( - line: string, -): RegExpMatchArray | null { - // add_ filter|action ( '" {hook} '" , - return line.match(/add_(?:filter|action)\(\s*['"](?\S+?)['"],\s*\w*?$/); -} +// Re-export from core for backward compatibility +export { + isInFilter, + isInAction, + isInFunctionDeclaration, +} from '../core/matchers.js'; diff --git a/src/utils/typeHelpers.ts b/src/utils/typeHelpers.ts index bc1d418..3a44ac3 100644 --- a/src/utils/typeHelpers.ts +++ b/src/utils/typeHelpers.ts @@ -1,93 +1,5 @@ -import { Tag, TagType } from '../types'; - -export function getTagType( - tag: Tag, -): TagType | null { - // https://www.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration - const allowedTypes: { [key: string]: number } = { - self: 5.0, - array: 5.1, - callable: 5.4, - bool: 7.0, - float: 7.0, - int: 7.0, - string: 7.0, - iterable: 7.1, - object: 7.2, - }; - - const typeData: TagType = { - type: '', - nullable: false, - }; - - // No type info? Bail. - if (!tag.types) { - return null; - } - - const types = [...tag.types]; - - // Handle nullable type. - if (types.length === 2) { - if (types[0] === 'null') { - types.splice(0, 1); - typeData.nullable = true; - } else if (types[1] === 'null') { - types.splice(1, 1); - typeData.nullable = true; - } - } - - // More than one type? Bail. - if (types.length !== 1) { - return null; - } - - let type = types[0]; - - // Un-hintable type? Bail. - if (['mixed'].includes(type)) { - return null; - } - - // Hinting for typed-arrays. - if (type.indexOf('[]') !== -1) { - type = 'array'; - } - - // Aliases for bool. - if (['false', 'true', 'boolean'].includes(type)) { - type = 'bool'; - } - - // Alias for callable. - if (type === 'callback') { - type = 'callable'; - } - - // Alias for int. - if (type === 'integer') { - type = 'int'; - } - - // Convert stdClass to object to avoid fatals when the stdClass gets promoted to a real class. - if (type === '\\stdClass') { - type = 'object'; - } - - // Check the allowed types, ignoring unknown types such as class and interface names. - if (allowedTypes[type]) { - return null; - } - - typeData.type = type; - - return typeData; -} - -export function getReturnType( - tag: Tag, -) : TagType | null { - return getTagType(tag); -} +// Re-export from core for backward compatibility +export { + getTagType, + getReturnType, +} from '../core/typeHelpers.js'; diff --git a/stryker.config.json b/stryker.config.json new file mode 100644 index 0000000..0d29435 --- /dev/null +++ b/stryker.config.json @@ -0,0 +1,34 @@ +{ + "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json", + "packageManager": "npm", + "reporters": ["html", "clear-text", "progress"], + "testRunner": "mocha", + "buildCommand": "npm run build", + "coverageAnalysis": "off", + "mochaOptions": { + "spec": ["out/test/unit/**/*.test.js"], + "ui": "tdd" + }, + "mutate": [ + "src/core/**/*.ts", + "!src/**/*.test.ts" + ], + "files": [ + "src/**/*.ts", + "package.json", + "tsconfig.json", + "node_modules/@wp-hooks/**/*" + ], + "timeoutMS": 60000, + "thresholds": { + "high": 80, + "low": 60, + "break": 50 + }, + "ignorePatterns": [ + ".vscode-test" + ], + "disableTypeChecks": "**/*.{js,ts,jsx,tsx,html,vue}", + "tempDirName": ".stryker-tmp", + "cleanTempDir": true +}