diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 80657da5dd7..d2a6507d76b 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -8,6 +8,8 @@ queries: uses: ./.github/codeql/queries paths: - ./extensions/ql-vscode + - ./.github/workflows + - ./.github/actions paths-ignore: - '**/node_modules' - '**/build' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c2cc47f75e8..4900e9a3798 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -142,12 +142,12 @@ jobs: node-version-file: extensions/ql-vscode/.nvmrc - name: Download artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: vscode-codeql-extension - name: Azure User-assigned managed identity login - uses: azure/login@v2 + uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -175,7 +175,7 @@ jobs: node-version-file: extensions/ql-vscode/.nvmrc - name: Download artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: vscode-codeql-extension diff --git a/docs/test-plan.md b/docs/test-plan.md index 904dda61b4c..90fe3cc2abf 100644 --- a/docs/test-plan.md +++ b/docs/test-plan.md @@ -45,7 +45,7 @@ choose to go through some of the Optional Test Cases. #### Test case 2: Running a problem query and viewing results 1. Open the [javascript ReDoS query](https://github.com/github/codeql/blob/main/javascript/ql/src/Performance/ReDoS.ql). -2. Select the `babel/babel` database (or download it if you don't have one already) +2. Select the `angular-cn/ng-nice` database (or download it if you don't have one already) 3. Run a local query. 4. Once the query completes: - Check that the result messages are rendered diff --git a/extensions/ql-vscode/.eslintignore b/extensions/ql-vscode/.eslintignore deleted file mode 100644 index a04d542eaa5..00000000000 --- a/extensions/ql-vscode/.eslintignore +++ /dev/null @@ -1,16 +0,0 @@ -.vscode-test/ -node_modules/ -out/ -build/ - -# Ignore js files -.eslintrc.js -jest.config.js -test/vscode-tests/activated-extension/jest-runner-vscode.config.js -test/vscode-tests/cli-integration/jest-runner-vscode.config.js -test/vscode-tests/jest-runner-vscode.config.base.js -test/vscode-tests/minimal-workspace/jest-runner-vscode.config.js -test/vscode-tests/no-workspace/jest-runner-vscode.config.js - -# Include the Storybook config -!.storybook diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js deleted file mode 100644 index eeac6beceb9..00000000000 --- a/extensions/ql-vscode/.eslintrc.js +++ /dev/null @@ -1,184 +0,0 @@ -const { resolve } = require("path"); - -const baseConfig = { - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: 2018, - sourceType: "module", - project: [ - resolve(__dirname, "tsconfig.lint.json"), - resolve(__dirname, "src/**/tsconfig.json"), - resolve(__dirname, "test/**/tsconfig.json"), - resolve(__dirname, "gulpfile.ts/tsconfig.json"), - resolve(__dirname, "scripts/tsconfig.json"), - resolve(__dirname, ".storybook/tsconfig.json"), - ], - }, - plugins: ["github", "@typescript-eslint", "etc"], - env: { - node: true, - es6: true, - }, - extends: [ - "eslint:recommended", - "plugin:github/recommended", - "plugin:github/typescript", - "plugin:jest-dom/recommended", - "plugin:prettier/recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript", - "plugin:deprecation/recommended", - ], - rules: { - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/no-unused-vars": [ - "warn", - { - vars: "all", - args: "none", - ignoreRestSiblings: false, - }, - ], - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }], - "@typescript-eslint/no-invalid-this": "off", - "@typescript-eslint/no-shadow": "off", - "prefer-const": ["warn", { destructuring: "all" }], - "@typescript-eslint/only-throw-error": "error", - "@typescript-eslint/consistent-type-imports": "error", - "import/consistent-type-specifier-style": ["error", "prefer-top-level"], - curly: ["error", "all"], - "escompat/no-regexp-lookbehind": "off", - "etc/no-implicit-any-catch": "error", - "filenames/match-regex": "off", - "i18n-text/no-en": "off", - "no-invalid-this": "off", - "no-console": "off", - "no-shadow": "off", - "github/array-foreach": "off", - "github/no-then": "off", - "react/jsx-key": ["error", { checkFragmentShorthand: true }], - "import/no-cycle": "error", - // Never allow extensions in import paths, except for JSON files where they are required. - "import/extensions": ["error", "never", { json: "always" }], - }, - settings: { - "import/resolver": { - typescript: true, - node: true, - }, - "import/extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], - // vscode and sarif don't exist on-disk, but only provide types. - "import/core-modules": ["vscode", "sarif"], - }, -}; - -module.exports = { - root: true, - ...baseConfig, - overrides: [ - { - files: ["src/stories/**/*"], - parserOptions: { - project: resolve(__dirname, "src/stories/tsconfig.json"), - }, - extends: [ - ...baseConfig.extends, - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:react-hooks/recommended", - "plugin:storybook/recommended", - "plugin:github/react", - ], - rules: { - ...baseConfig.rules, - }, - settings: { - react: { - version: "detect", - }, - }, - }, - { - files: ["src/view/**/*"], - parserOptions: { - project: resolve(__dirname, "src/view/tsconfig.json"), - }, - extends: [ - ...baseConfig.extends, - "plugin:react/recommended", - "plugin:react/jsx-runtime", - "plugin:react-hooks/recommended", - "plugin:github/react", - ], - rules: { - ...baseConfig.rules, - }, - settings: { - react: { - version: "detect", - }, - }, - }, - { - files: ["test/vscode-tests/**/*"], - parserOptions: { - project: resolve(__dirname, "test/tsconfig.json"), - }, - env: { - jest: true, - }, - rules: { - ...baseConfig.rules, - // We want to allow mocking of functions in modules, so we need to allow namespace imports. - "import/no-namespace": "off", - "@typescript-eslint/no-unsafe-function-type": "off", - }, - }, - { - files: ["test/**/*"], - parserOptions: { - project: resolve(__dirname, "test/tsconfig.json"), - }, - env: { - jest: true, - }, - rules: { - "@typescript-eslint/no-explicit-any": "off", - }, - }, - { - files: [ - ".eslintrc.js", - "test/**/jest-runner-vscode.config.js", - "test/**/jest-runner-vscode.config.base.js", - ], - parser: undefined, - plugins: ["github"], - extends: [ - "eslint:recommended", - "plugin:github/recommended", - "plugin:prettier/recommended", - ], - rules: { - "import/no-commonjs": "off", - "prefer-template": "off", - "filenames/match-regex": "off", - "@typescript-eslint/no-var-requires": "off", - }, - }, - { - files: [".storybook/**/*.tsx"], - parserOptions: { - project: resolve(__dirname, ".storybook/tsconfig.json"), - }, - rules: { - ...baseConfig.rules, - // Storybook doesn't use the automatic JSX runtime in the addon yet, so we need to allow - // `React` to be imported. - "import/no-namespace": ["error", { ignore: ["react"] }], - }, - }, - ], -}; diff --git a/extensions/ql-vscode/.nvmrc b/extensions/ql-vscode/.nvmrc index 3f35247103b..6edc5a20f3f 100644 --- a/extensions/ql-vscode/.nvmrc +++ b/extensions/ql-vscode/.nvmrc @@ -1 +1 @@ -v20.18.2 +v22.17.0 diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 03054c92082..0b3c1bfb4c2 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,17 @@ ## [UNRELEASED] +- Add new command "CodeQL: Trim Overlay Base Cache" that returns a database to the state prior to overlay evaluation, leaving only base predicates and types that may later be referenced during overlay evaluation. [#4082](https://github.com/github/vscode-codeql/pull/4082) +- Remove support for CodeQL CLI versions older than 2.19.4. [#4108](https://github.com/github/vscode-codeql/pull/4108) + +## 1.17.4 - 10 July 2025 + +- Fix variant analysis pack creation on some Windows systems [#4068](https://github.com/github/vscode-codeql/pull/4068) + +## 1.17.3 - 3 June 2025 + +- Fix reporting of bad join orders in recursive predicates. [#4019](https://github.com/github/vscode-codeql/pull/4019) + ## 1.17.2 - 27 March 2025 - Always authenticate when downloading databases from GitHub, instead of only when in canary mode. [#3941](https://github.com/github/vscode-codeql/pull/3941) diff --git a/extensions/ql-vscode/eslint.config.mjs b/extensions/ql-vscode/eslint.config.mjs new file mode 100644 index 00000000000..ba67c3a224a --- /dev/null +++ b/extensions/ql-vscode/eslint.config.mjs @@ -0,0 +1,202 @@ +import { resolve, dirname } from "path"; +import { fileURLToPath } from "url"; +import eslint from "@eslint/js"; +import { globalIgnores } from "eslint/config"; +import github from "eslint-plugin-github"; +import tseslint from "typescript-eslint"; +import eslintPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import * as jestDom from "eslint-plugin-jest-dom"; +import * as typescriptESLintParser from "@typescript-eslint/parser"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; +import storybook from "eslint-plugin-storybook"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default tseslint.config( + globalIgnores([ + ".vscode-test/", + "node_modules/", + "out/", + "build/", + "scripts/", + "**/jest.config.ts", + "**/jest.config.js", + "**/jest-runner-vscode.config.js", + "**/jest-runner-vscode.config.base.js", + ".markdownlint-cli2.cjs", + "eslint.config.mjs", + "!.storybook", + ]), + github.getFlatConfigs().recommended, + ...github.getFlatConfigs().typescript, + tseslint.configs.recommendedTypeChecked, + eslint.configs.recommended, + tseslint.configs.recommended, + jestDom.configs["flat/recommended"], + { + rules: { + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + vars: "all", + args: "none", + ignoreRestSiblings: false, + }, + ], + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }], + "@typescript-eslint/no-invalid-this": "off", + "@typescript-eslint/no-shadow": "off", + "prefer-const": ["warn", { destructuring: "all" }], + "@typescript-eslint/only-throw-error": "error", + "@typescript-eslint/consistent-type-imports": "error", + "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + curly: ["error", "all"], + "escompat/no-regexp-lookbehind": "off", + "filenames/match-regex": "off", + "i18n-text/no-en": "off", + "no-invalid-this": "off", + "no-console": "off", + "no-shadow": "off", + "github/array-foreach": "off", + "github/no-then": "off", + // "react/jsx-key": ["error", { checkFragmentShorthand: true }], + "import/no-cycle": "error", + // Never allow extensions in import paths, except for JSON files where they are required. + "import/extensions": ["error", "never", { json: "always" }], + + // Rules disabled during eslint 9 migration + "github/filenames-match-regex": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-base-to-string": "off", + "@typescript-eslint/no-array-delete": "off", + }, + settings: { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"] + }, + "import/resolver": { + typescript: true, + node: true, + }, + "import/extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], + // vscode and sarif don't exist on-disk, but only provide types. + "import/core-modules": ["vscode", "sarif"], + }, + languageOptions: { + parser: typescriptESLintParser, + parserOptions: { + ecmaVersion: 2018, + sourceType: "module", + projectService: true, + tsconfigRootDir: import.meta.dirname, + project: resolve(__dirname, "tsconfig.lint.json"), + }, + }, + }, + { + files: ["src/stories/**/*"], + languageOptions: { + parserOptions: { + project: resolve(__dirname, "src/stories/tsconfig.json"), + }, + }, + extends: [ + react.configs.flat.recommended, + react.configs.flat['jsx-runtime'], + reactHooks.configs['recommended-latest'], + storybook.configs['flat/recommended'], + github.getFlatConfigs().react, + ], + settings: { + react: { + version: "detect", + }, + }, + }, + { + files: ["src/view/**/*"], + languageOptions: { + parserOptions: { + project: resolve(__dirname, "src/view/tsconfig.json"), + }, + }, + extends: [ + react.configs.flat.recommended, + react.configs.flat['jsx-runtime'], + reactHooks.configs['recommended-latest'], + github.getFlatConfigs().react, + ], + settings: { + react: { + version: "detect", + }, + }, + }, + { + files: ["test/vscode-tests/**/*"], + languageOptions: { + parserOptions: { + project: resolve(__dirname, "test/tsconfig.json"), + }, + globals: { + jest: true, + }, + }, + rules: { + // We want to allow mocking of functions in modules, so we need to allow namespace imports. + "import/no-namespace": "off", + "@typescript-eslint/no-unsafe-function-type": "off", + }, + }, + { + files: ["test/**/*"], + languageOptions: { + parserOptions: { + project: resolve(__dirname, "test/tsconfig.json"), + }, + globals: { + jest: true, + }, + }, + rules: { + "@typescript-eslint/no-explicit-any": "off", + + // Rules disabled during eslint 9 migration + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/unbound-method": "off", + }, + }, + { + files: [".storybook/**/*", "src/stories/**/*"], + languageOptions: { + parserOptions: { + project: resolve(__dirname, ".storybook/tsconfig.json"), + }, + }, + rules: { + // Storybook doesn't use the automatic JSX runtime in the addon yet, so we need to allow + // `React` to be imported. + "import/no-namespace": ["error", { ignore: ["react"] }], + + // Rules disabled during eslint 9 migration + "@typescript-eslint/no-unsafe-argument": "off", + "storybook/no-renderer-packages": "off", + "storybook/story-exports": "off", + }, + }, + eslintPrettierRecommended, +); diff --git a/extensions/ql-vscode/gulpfile.ts/appInsights.ts b/extensions/ql-vscode/gulpfile.ts/appInsights.ts index e5dba3fd17e..86d36aae4fe 100644 --- a/extensions/ql-vscode/gulpfile.ts/appInsights.ts +++ b/extensions/ql-vscode/gulpfile.ts/appInsights.ts @@ -1,6 +1,5 @@ import { src, dest } from "gulp"; -// eslint-disable-next-line @typescript-eslint/no-require-imports,import/no-commonjs -const replace = require("gulp-replace"); +import replace from "gulp-replace"; /** Inject the application insights key into the telemetry file */ export function injectAppInsightsKey() { diff --git a/extensions/ql-vscode/gulpfile.ts/deploy.ts b/extensions/ql-vscode/gulpfile.ts/deploy.ts index da02b9fd494..7b98600815b 100644 --- a/extensions/ql-vscode/gulpfile.ts/deploy.ts +++ b/extensions/ql-vscode/gulpfile.ts/deploy.ts @@ -4,6 +4,7 @@ import { mkdirs, readdir, unlinkSync, + rename, remove, writeFile, } from "fs-extra"; @@ -45,6 +46,10 @@ async function copyPackage( copyDirectory(resolve(sourcePath, file), resolve(destPath, file)), ), ); + + // The koffi directory needs to be located at the root of the package to ensure + // that the koffi package can find its native modules. + await rename(resolve(destPath, "out", "koffi"), resolve(destPath, "koffi")); } export async function deployPackage(): Promise { diff --git a/extensions/ql-vscode/gulpfile.ts/index.ts b/extensions/ql-vscode/gulpfile.ts/index.ts index d3084561cea..3c20b838c09 100644 --- a/extensions/ql-vscode/gulpfile.ts/index.ts +++ b/extensions/ql-vscode/gulpfile.ts/index.ts @@ -5,7 +5,7 @@ import { checkTypeScript, watchCheckTypeScript, cleanOutput, - copyWasmFiles, + copyModules, } from "./typescript"; import { compileTextMateGrammar } from "./textmate"; import { packageExtension } from "./package"; @@ -21,7 +21,7 @@ export const buildWithoutPackage = series( cleanOutput, parallel( compileEsbuild, - copyWasmFiles, + copyModules, checkTypeScript, compileTextMateGrammar, compileViewEsbuild, @@ -46,7 +46,7 @@ export { watchCheckTypeScript, watchViewEsbuild, compileEsbuild, - copyWasmFiles, + copyModules, checkTypeScript, injectAppInsightsKey, compileViewEsbuild, diff --git a/extensions/ql-vscode/gulpfile.ts/textmate.ts b/extensions/ql-vscode/gulpfile.ts/textmate.ts index 0b7d2b88fac..a2ed4db083b 100644 --- a/extensions/ql-vscode/gulpfile.ts/textmate.ts +++ b/extensions/ql-vscode/gulpfile.ts/textmate.ts @@ -9,6 +9,7 @@ import type { Pattern, TextmateGrammar, } from "./textmate-grammar"; +import { pipeline } from "stream/promises"; /** * Replaces all rule references with the match pattern of the referenced rule. @@ -22,7 +23,6 @@ function replaceReferencesWithStrings( replacements: Map, ): string { let result = value; - // eslint-disable-next-line no-constant-condition while (true) { const original = result; for (const key of Array.from(replacements.keys())) { @@ -66,8 +66,7 @@ function getNodeMatchText(rule: Pattern): string { } else if (rule.patterns !== undefined) { const patterns: string[] = []; // For a list of patterns, use the disjunction of those patterns. - for (const patternIndex in rule.patterns) { - const pattern = rule.patterns[patternIndex]; + for (const pattern of rule.patterns) { if (pattern.include !== null) { patterns.push(`(?${pattern.include})`); } @@ -180,11 +179,11 @@ function expandPatternMatchProperties( const patterns: string[] = Array.isArray(pattern) ? pattern : [pattern]; rule[key] = patterns.map((p) => `((?${p}))`).join("|") as T; const captures: Pattern["captures"] = {}; - for (const patternIndex in patterns) { - captures[(Number(patternIndex) + 1).toString()] = { + for (const [captureIndex, capture] of patterns.entries()) { + captures[(Number(captureIndex) + 1).toString()] = { patterns: [ { - include: patterns[patternIndex], + include: capture, }, ], }; @@ -276,7 +275,9 @@ export function transpileTextMateGrammar() { } export function compileTextMateGrammar() { - return src("syntaxes/*.tmLanguage.yml") - .pipe(transpileTextMateGrammar()) - .pipe(dest("out/syntaxes")); + return pipeline( + src("syntaxes/*.tmLanguage.yml"), + transpileTextMateGrammar(), + dest("out/syntaxes"), + ); } diff --git a/extensions/ql-vscode/gulpfile.ts/typescript.ts b/extensions/ql-vscode/gulpfile.ts/typescript.ts index c7f12785507..9d49b56821e 100644 --- a/extensions/ql-vscode/gulpfile.ts/typescript.ts +++ b/extensions/ql-vscode/gulpfile.ts/typescript.ts @@ -1,9 +1,10 @@ import { gray, red } from "ansi-colors"; -import { dest, src, watch } from "gulp"; +import { dest, parallel, src, watch } from "gulp"; import esbuild from "gulp-esbuild"; import type { reporter } from "gulp-typescript"; import { createProject } from "gulp-typescript"; import del from "del"; +import { pipeline } from "stream/promises"; export function goodReporter(): reporter.Reporter { return { @@ -37,23 +38,23 @@ export function cleanOutput() { } export function compileEsbuild() { - return src("./src/extension.ts") - .pipe( - esbuild({ - outfile: "extension.js", - bundle: true, - external: ["vscode", "fsevents"], - format: "cjs", - platform: "node", - target: "es2020", - sourcemap: "linked", - sourceRoot: "..", - loader: { - ".node": "copy", - }, - }), - ) - .pipe(dest("out")); + return pipeline( + src("./src/extension.ts"), + esbuild({ + outfile: "extension.js", + bundle: true, + external: ["vscode", "fsevents"], + format: "cjs", + platform: "node", + target: "es2020", + sourcemap: "linked", + sourceRoot: "..", + loader: { + ".node": "copy", + }, + }), + dest("out"), + ); } export function watchEsbuild() { @@ -70,7 +71,7 @@ export function watchCheckTypeScript() { watch(["src/**/*.ts", "!src/view/**/*.ts"], checkTypeScript); } -export function copyWasmFiles() { +function copyWasmFiles() { // We need to copy this file for the source-map package to work. Without this fie, the source-map // package is not able to load the WASM file because we are not including the full node_modules // directory. In version 0.7.4, it is not possible to call SourceMapConsumer.initialize in Node environments @@ -82,3 +83,18 @@ export function copyWasmFiles() { encoding: false, }).pipe(dest("out")); } + +function copyNativeAddonFiles() { + // We need to copy these files manually because we only want to include Windows x64 to limit + // the size of the extension. Windows x64 is the most common platform that requires short path + // expansion, so we only include this platform. + // See src/common/short-paths.ts + return pipeline( + src("node_modules/koffi/build/koffi/win32_x64/*.node", { + encoding: false, + }), + dest("out/koffi/win32_x64"), + ); +} + +export const copyModules = parallel(copyWasmFiles, copyNativeAddonFiles); diff --git a/extensions/ql-vscode/gulpfile.ts/view.ts b/extensions/ql-vscode/gulpfile.ts/view.ts index fea09031d70..698f39c113e 100644 --- a/extensions/ql-vscode/gulpfile.ts/view.ts +++ b/extensions/ql-vscode/gulpfile.ts/view.ts @@ -3,28 +3,29 @@ import esbuild from "gulp-esbuild"; import { createProject } from "gulp-typescript"; import { goodReporter } from "./typescript"; +import { pipeline } from "stream/promises"; import chromiumVersion from "./chromium-version.json"; const tsProject = createProject("src/view/tsconfig.json"); export function compileViewEsbuild() { - return src("./src/view/webview.tsx") - .pipe( - esbuild({ - outfile: "webview.js", - bundle: true, - format: "iife", - platform: "browser", - target: `chrome${chromiumVersion.chromiumVersion}`, - jsx: "automatic", - sourcemap: "linked", - sourceRoot: "..", - loader: { - ".ttf": "file", - }, - }), - ) - .pipe(dest("out")); + return pipeline( + src("./src/view/webview.tsx"), + esbuild({ + outfile: "webview.js", + bundle: true, + format: "iife", + platform: "browser", + target: `chrome${chromiumVersion.chromiumVersion}`, + jsx: "automatic", + sourcemap: "linked", + sourceRoot: "..", + loader: { + ".ttf": "file", + }, + }), + dest("out"), + ); } export function watchViewEsbuild() { diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 76aaa93ff56..7ded4c763a8 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.17.3", + "version": "1.17.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.17.3", + "version": "1.17.5", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -14,17 +14,18 @@ "@octokit/plugin-retry": "^7.2.0", "@octokit/plugin-throttling": "^9.6.0", "@octokit/rest": "^21.1.1", + "@vscode-elements/react-elements": "^0.9.0", "@vscode/codicons": "^0.0.36", "@vscode/debugadapter": "^1.59.0", "@vscode/debugprotocol": "^1.68.0", - "@vscode/webview-ui-toolkit": "^1.0.1", "ajv": "^8.11.0", "chokidar": "^3.6.0", "d3": "^7.9.0", "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", - "msw": "^2.6.8", + "koffi": "^2.12.0", + "msw": "^2.7.4", "nanoid": "^5.0.7", "p-queue": "^8.0.1", "proper-lockfile": "^4.1.2", @@ -35,7 +36,7 @@ "source-map-support": "^0.5.21", "stream-json": "^1.7.3", "styled-components": "^6.1.13", - "tmp": "^0.2.1", + "tmp": "^0.2.5", "tmp-promise": "^3.0.2", "tree-kill": "^1.2.2", "vscode-extension-telemetry": "^0.1.6", @@ -45,34 +46,35 @@ "zip-a-folder": "^3.1.6" }, "devDependencies": { - "@babel/core": "^7.24.6", + "@babel/core": "^7.28.0", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", + "@eslint/js": "^9.28.0", "@faker-js/faker": "^9.0.3", "@github/markdownlint-github": "^0.6.3", "@jest/environment": "^30.0.0-alpha.7", "@jest/environment-jsdom-abstract": "^30.0.0-alpha.7", "@microsoft/eslint-formatter-sarif": "^3.1.0", "@playwright/test": "^1.50.1", - "@storybook/addon-a11y": "^8.6.10", - "@storybook/addon-actions": "^8.6.10", - "@storybook/addon-essentials": "^8.6.10", - "@storybook/addon-interactions": "^8.6.10", - "@storybook/addon-links": "^8.6.10", + "@storybook/addon-a11y": "^8.6.14", + "@storybook/addon-actions": "^8.6.14", + "@storybook/addon-essentials": "^8.6.14", + "@storybook/addon-interactions": "^8.6.14", + "@storybook/addon-links": "^8.6.14", "@storybook/blocks": "^8.6.0", - "@storybook/components": "^8.6.10", + "@storybook/components": "^8.6.14", "@storybook/csf": "^0.1.13", "@storybook/icons": "^1.4.0", - "@storybook/manager-api": "^8.6.10", - "@storybook/react": "^8.6.10", - "@storybook/react-vite": "^8.6.10", - "@storybook/theming": "^8.6.10", + "@storybook/manager-api": "^8.6.14", + "@storybook/react": "^8.6.14", + "@storybook/react-vite": "^8.6.14", + "@storybook/theming": "^8.6.14", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.1.0", - "@testing-library/user-event": "^14.5.2", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/cross-spawn": "^6.0.6", "@types/d3": "^7.4.0", "@types/d3-graphviz": "^2.6.6", @@ -82,7 +84,7 @@ "@types/gulp-replace": "^1.1.0", "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.6", - "@types/node": "20.17.*", + "@types/node": "22.17.*", "@types/proper-lockfile": "^4.1.4", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -95,8 +97,8 @@ "@types/tmp": "^0.2.6", "@types/vscode": "1.90.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", + "@typescript-eslint/eslint-plugin": "^8.39.0", + "@typescript-eslint/parser": "^8.39.0", "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^3.2.1", "ansi-colors": "^4.1.1", @@ -105,24 +107,22 @@ "cross-env": "^7.0.3", "cross-spawn": "^7.0.6", "del": "^6.0.0", - "eslint": "^8.56.0", + "eslint": "^9.28.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-etc": "^2.0.2", - "eslint-plugin-github": "^5.0.1", - "eslint-plugin-import": "^2.31.0", + "eslint-plugin-github": "^6.0.0", "eslint-plugin-jest-dom": "^5.5.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-storybook": "^0.8.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-storybook": "^9.0.12", "glob": "^11.0.1", "gulp": "^5.0.0", "gulp-esbuild": "^0.14.0", "gulp-replace": "^1.1.3", "gulp-typescript": "^5.0.1", - "husky": "^9.1.5", + "husky": "^9.1.7", "jest": "^30.0.0-alpha.7", "jest-runner-vscode": "^3.0.1", "jsdom": "^26.0.0", @@ -131,20 +131,21 @@ "markdownlint-cli2-formatter-pretty": "^0.0.7", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", - "prettier": "^3.2.5", - "storybook": "^8.6.10", + "prettier": "^3.6.1", + "storybook": "^8.6.14", "tar-stream": "^3.1.7", "through2": "^4.0.2", - "ts-jest": "^29.2.5", + "ts-jest": "^29.3.2", "ts-json-schema-generator": "^2.3.0", "ts-node": "^10.9.2", "ts-unused-exports": "^10.1.0", "typescript": "^5.6.2", - "vite": "^6.2.4", + "typescript-plugin-css-modules": "^5.1.0", + "vite": "^6.3.4", "vite-node": "^3.0.7" }, "engines": { - "node": "^20.18.2", + "node": "^22.17.0", "npm": ">=7.20.6", "vscode": "^1.90.0" } @@ -159,17 +160,18 @@ } }, "node_modules/@achrinza/node-ipc": { - "version": "9.2.8", - "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.8.tgz", - "integrity": "sha512-DSzEEkbMYbAUVlhy7fg+BzccoRuSQzqHbIPGxGv19OJ2WKwS3/9ChAnQcII4g+GujcHhyJ8BUuOVAx/S5uAfQg==", + "version": "9.2.9", + "resolved": "https://registry.npmjs.org/@achrinza/node-ipc/-/node-ipc-9.2.9.tgz", + "integrity": "sha512-7s0VcTwiK/0tNOVdSX9FWMeFdOEcsAOz9HesBldXxFMaGvIak7KC2z9tV9EgsQXn6KUsWsfIkViMNuIo0GoZDQ==", "dev": true, + "license": "MIT", "dependencies": { "@node-ipc/js-queue": "2.0.3", "event-pubsub": "4.3.0", "js-message": "1.0.7" }, "engines": { - "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21" + "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" } }, "node_modules/@adobe/css-tools": { @@ -473,16 +475,6 @@ "node": ">= 14" } }, - "node_modules/@azure/identity/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@azure/identity/node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -607,44 +599,47 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "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.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", - "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", - "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.6", - "@babel/generator": "^7.24.6", - "@babel/helper-compilation-targets": "^7.24.6", - "@babel/helper-module-transforms": "^7.24.6", - "@babel/helpers": "^7.24.6", - "@babel/parser": "^7.24.6", - "@babel/template": "^7.24.6", - "@babel/traverse": "^7.24.6", - "@babel/types": "^7.24.6", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -669,15 +664,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -709,14 +705,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", - "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", + "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.24.6", - "@babel/helper-validator-option": "^7.24.6", - "browserslist": "^4.22.2", + "@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" }, @@ -831,6 +828,16 @@ "node": ">=6.9.0" } }, + "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-hoist-variables": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", @@ -858,28 +865,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "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.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -972,28 +980,31 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "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.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "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.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "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" } @@ -1013,25 +1024,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", - "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.6", - "@babel/types": "^7.24.6" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -2428,45 +2441,48 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "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.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@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.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3085,39 +3101,101 @@ } }, "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==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/compat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.3.1.tgz", + "integrity": "sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^8.40 || 9" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -3125,7 +3203,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -3136,6 +3214,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3148,15 +3227,13 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3166,27 +3243,44 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://eslint.org/donate" } }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@faker-js/faker": { @@ -3307,14 +3401,54 @@ "dot-wasm": "bin/dot-wasm.js" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -3335,10 +3469,26 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@inquirer/confirm": { "version": "5.1.0", @@ -4904,9 +5054,9 @@ } }, "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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": { @@ -5004,17 +5154,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -5026,15 +5173,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -5042,15 +5180,40 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", + "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/react": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.7.tgz", + "integrity": "sha512-cencnwwLXQKiKxjfFzSgZRngcWJzUDZi/04E0fSaF86wZgchMdvTyu+lE36DrUfvuus3bH8+xLPrhM1cTjwpzw==", + "license": "BSD-3-Clause", + "peerDependencies": { + "@types/react": "17 || 18 || 19" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, "node_modules/@mdx-js/react": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", @@ -5090,45 +5253,293 @@ "node": ">= 14" } }, - "node_modules/@microsoft/fast-element": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.12.0.tgz", - "integrity": "sha512-gQutuDHPKNxUEcQ4pypZT4Wmrbapus+P9s3bR/SEOLsMbNqNoXigGImITygI5zhb+aA5rzflM6O8YWkmRbGkPA==" - }, - "node_modules/@microsoft/fast-foundation": { - "version": "2.49.4", - "resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.49.4.tgz", - "integrity": "sha512-5I2tSPo6bnOfVAIX7XzX+LhilahwvD7h+yzl3jW0t5IYmMX9Lci9VUVyx5f8hHdb1O9a8Y9Atb7Asw7yFO/u+w==", + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@microsoft/fast-element": "^1.12.0", - "@microsoft/fast-web-utilities": "^5.4.1", - "tabbable": "^5.2.0", - "tslib": "^1.13.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "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/@microsoft/fast-foundation/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } }, - "node_modules/@microsoft/fast-react-wrapper": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.3.22.tgz", - "integrity": "sha512-XhlX4m6znh7XW92oPvlKoG9USUn9JtF9rP1qtUoIbkaDaFtUS+H8o1Jn6/oK/rS44LbBLJXrvRkInmSWlDiGFw==", + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", "dependencies": { - "@microsoft/fast-element": "^1.12.0", - "@microsoft/fast-foundation": "^2.49.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "peerDependencies": { - "react": ">=16.9.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@microsoft/fast-web-utilities": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz", - "integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==", + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", "dependencies": { - "exenv-es6": "^1.1.1" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@microsoft/eslint-formatter-sarif/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@mswjs/interceptors": { @@ -5497,76 +5908,370 @@ "node": ">=14" } }, - "node_modules/@phenomnomnominal/tsquery": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", - "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, "dependencies": { - "esquery": "^1.4.0" + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" }, - "peerDependencies": { - "typescript": "^3 || ^4 || ^5" - } - }, - "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==", - "optional": true, "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", - "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@pkgr/utils/node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@phenomnomnominal/tsquery": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-5.0.1.tgz", + "integrity": "sha512-3nVv+e2FQwsW8Aw6qTU6f+1rfcJ3hrcnvH/mu9i8YhxO+9sqbOfpL8m6PbET5+xKOlz/VSbp0RoYWYCtIsnmuA==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "esquery": "^1.4.0" }, + "peerDependencies": { + "typescript": "^3 || ^4 || ^5" + } + }, + "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==", + "optional": true, "engines": { - "node": ">=14.16" + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/unts" } }, "node_modules/@playwright/test": { @@ -5608,9 +6313,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", - "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", + "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", "cpu": [ "arm" ], @@ -5622,9 +6327,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", - "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", + "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", "cpu": [ "arm64" ], @@ -5636,9 +6341,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", - "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", + "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", "cpu": [ "arm64" ], @@ -5650,9 +6355,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", - "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", + "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", "cpu": [ "x64" ], @@ -5664,9 +6369,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", - "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", + "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", "cpu": [ "arm64" ], @@ -5678,9 +6383,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", - "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", + "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", "cpu": [ "x64" ], @@ -5692,9 +6397,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", - "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", + "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", "cpu": [ "arm" ], @@ -5706,9 +6411,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", - "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", + "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", "cpu": [ "arm" ], @@ -5720,9 +6425,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", - "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", + "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", "cpu": [ "arm64" ], @@ -5734,9 +6439,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", - "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", + "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", "cpu": [ "arm64" ], @@ -5748,9 +6453,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", - "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", + "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", "cpu": [ "loong64" ], @@ -5762,9 +6467,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", - "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", + "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", "cpu": [ "ppc64" ], @@ -5776,9 +6481,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", - "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", + "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", + "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", "cpu": [ "riscv64" ], @@ -5790,9 +6509,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", - "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", + "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", "cpu": [ "s390x" ], @@ -5804,9 +6523,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", - "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", + "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", "cpu": [ "x64" ], @@ -5818,9 +6537,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", - "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", + "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", "cpu": [ "x64" ], @@ -5832,9 +6551,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", - "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", + "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", "cpu": [ "arm64" ], @@ -5846,9 +6565,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", - "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", + "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", "cpu": [ "ia32" ], @@ -5860,9 +6579,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", - "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", + "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", "cpu": [ "x64" ], @@ -5918,15 +6637,15 @@ } }, "node_modules/@storybook/addon-a11y": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.6.10.tgz", - "integrity": "sha512-g+p0soI03rshuLhBtjvRXfEuj0IxfX5RRIkHKaqpIKKRL8WVVdtLoxkEjOUo5zIAodmZKFEfYLC8+ELR7fmSjw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.6.14.tgz", + "integrity": "sha512-fozv6enO9IgpWq2U8qqS8MZ21Nt+MVHiRQe3CjnCpBOejTyo/ATm690PeYYRVHVG6M/15TVePb0h3ngKQbrrzQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-highlight": "8.6.10", + "@storybook/addon-highlight": "8.6.14", "@storybook/global": "^5.0.0", - "@storybook/test": "8.6.10", + "@storybook/test": "8.6.14", "axe-core": "^4.2.0" }, "funding": { @@ -5934,13 +6653,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-actions": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.6.10.tgz", - "integrity": "sha512-g+aC1bFwIwZqxSVjw+BhJJeTmZPBhZT52AO6DUYWF+FZ2N3rjnaVKwT1gZYYhuYw2WtWw2wLivkXcq2L/IBbkg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.6.14.tgz", + "integrity": "sha512-mDQxylxGGCQSK7tJPkD144J8jWh9IU9ziJMHfB84PKpI/V5ZgqMDnpr2bssTrUaGDqU5e1/z8KcRF+Melhs9pQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5955,13 +6674,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-backgrounds": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.6.10.tgz", - "integrity": "sha512-zou/VJSVDacuaQVJV22hbQEZrQBllcoxSw40EgSedqLv1qaVpC7Nz5LY9srl522LeoVEP+AOYKf9K7hLbm0o7w==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.6.14.tgz", + "integrity": "sha512-l9xS8qWe5n4tvMwth09QxH2PmJbCctEvBAc1tjjRasAfrd69f7/uFK4WhwJAstzBTNgTc8VXI4w8ZR97i1sFbg==", "dev": true, "license": "MIT", "dependencies": { @@ -5974,13 +6693,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-controls": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.6.10.tgz", - "integrity": "sha512-aMw3NCVSq+vWEAp10kbBlbMx+7PIFFdgxMCh7b9N2DUR/5J4KCNFQosa8fAn03Noh2g5jgceqNyY6L4lDIu0Xw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.6.14.tgz", + "integrity": "sha512-IiQpkNJdiRyA4Mq9mzjZlvQugL/aE7hNgVxBBGPiIZG6wb6Ht9hNnBYpap5ZXXFKV9p2qVI0FZK445ONmAa+Cw==", "dev": true, "license": "MIT", "dependencies": { @@ -5993,20 +6712,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-docs": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.6.10.tgz", - "integrity": "sha512-VnGtzwVaC8NvfhLr8UdIa6n38emU2SaDzfOTbvR1zibiqRmFDbo+kvAGSPOT6oIC5jZleUTLiIz0GzHxLuMxOQ==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.6.14.tgz", + "integrity": "sha512-Obpd0OhAF99JyU5pp5ci17YmpcQtMNgqW2pTXV8jAiiipWpwO++hNDeQmLmlSXB399XjtRDOcDVkoc7rc6JzdQ==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.6.10", - "@storybook/csf-plugin": "8.6.10", - "@storybook/react-dom-shim": "8.6.10", + "@storybook/blocks": "8.6.14", + "@storybook/csf-plugin": "8.6.14", + "@storybook/react-dom-shim": "8.6.14", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -6016,25 +6735,25 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-essentials": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.6.10.tgz", - "integrity": "sha512-8sKfAp3KkEjIHm02JhdazEKHlwO8VQgzAHk2fzHREgf24KqlCyF9BhDM1vG38fGdB+B+l1edZTE5a3NmcvK5Cg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.6.14.tgz", + "integrity": "sha512-5ZZSHNaW9mXMOFkoPyc3QkoNGdJHETZydI62/OASR0lmPlJ1065TNigEo5dJddmZNn0/3bkE8eKMAzLnO5eIdA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-actions": "8.6.10", - "@storybook/addon-backgrounds": "8.6.10", - "@storybook/addon-controls": "8.6.10", - "@storybook/addon-docs": "8.6.10", - "@storybook/addon-highlight": "8.6.10", - "@storybook/addon-measure": "8.6.10", - "@storybook/addon-outline": "8.6.10", - "@storybook/addon-toolbars": "8.6.10", - "@storybook/addon-viewport": "8.6.10", + "@storybook/addon-actions": "8.6.14", + "@storybook/addon-backgrounds": "8.6.14", + "@storybook/addon-controls": "8.6.14", + "@storybook/addon-docs": "8.6.14", + "@storybook/addon-highlight": "8.6.14", + "@storybook/addon-measure": "8.6.14", + "@storybook/addon-outline": "8.6.14", + "@storybook/addon-toolbars": "8.6.14", + "@storybook/addon-viewport": "8.6.14", "ts-dedent": "^2.0.0" }, "funding": { @@ -6042,13 +6761,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-highlight": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.6.10.tgz", - "integrity": "sha512-ZKl0yKzs/6xOpeDIiqHhfrJGQYA7jQ6cxO2nUm3zyqOnHZspef38VlqE63VttBq+mKnh9VbemmaTd2mUgQnm2A==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.6.14.tgz", + "integrity": "sha512-4H19OJlapkofiE9tM6K/vsepf4ir9jMm9T+zw5L85blJZxhKZIbJ6FO0TCG9PDc4iPt3L6+aq5B0X29s9zicNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6059,19 +6778,19 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-interactions": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.6.10.tgz", - "integrity": "sha512-BtuqLJj1L5a8a4RmnX5YjrGhiEfn7LTdQgn2m71F8DnMCwvvYLHQgYUcpjobMld1OZr3IKq4/zCqesaGET++fQ==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.6.14.tgz", + "integrity": "sha512-8VmElhm2XOjh22l/dO4UmXxNOolGhNiSpBcls2pqWSraVh4a670EyYBZsHpkXqfNHo2YgKyZN3C91+9zfH79qQ==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.6.10", - "@storybook/test": "8.6.10", + "@storybook/instrumenter": "8.6.14", + "@storybook/test": "8.6.14", "polished": "^4.2.2", "ts-dedent": "^2.2.0" }, @@ -6080,13 +6799,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-links": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.6.10.tgz", - "integrity": "sha512-t7gRsFbOIAsqxb/5KA/LOywvx8USopqfW1KwLDBrDYaRUwkdiJVOSxjKB1a6cndFmqcGzucdXQx/PMmOQe9dig==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.6.14.tgz", + "integrity": "sha512-DRlXHIyZzOruAZkxmXfVgTF+4d6K27pFcH4cUsm3KT1AXuZbr23lb5iZHpUZoG6lmU85Sru4xCEgewSTXBIe1w==", "dev": true, "license": "MIT", "dependencies": { @@ -6099,7 +6818,7 @@ }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.10" + "storybook": "^8.6.14" }, "peerDependenciesMeta": { "react": { @@ -6108,9 +6827,9 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.6.10.tgz", - "integrity": "sha512-ef5vAum7tMdiTsGsHOIHaLCyN0e3gLU2X4gzNelqH0/x/09C2QQaiOFDIpvbKt6HSjpHJeYcUOGzF7U/o4xVkw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.6.14.tgz", + "integrity": "sha512-1Tlyb72NX8aAqm6I6OICsUuGOP6hgnXcuFlXucyhKomPa6j3Eu2vKu561t/f0oGtAK2nO93Z70kVaEh5X+vaGw==", "dev": true, "license": "MIT", "dependencies": { @@ -6122,13 +6841,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-outline": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.6.10.tgz", - "integrity": "sha512-Z5lQ/q9rULtlD99V1S3ymEU59tJGD2KHEdr4HRUgxo+fkyy7nOZDi88sOupoICBuAVYBIcxLKiMeYrUIwjHqtg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.6.14.tgz", + "integrity": "sha512-CW857JvN6OxGWElqjlzJO2S69DHf+xO3WsEfT5mT3ZtIjmsvRDukdWfDU9bIYUFyA2lFvYjncBGjbK+I91XR7w==", "dev": true, "license": "MIT", "dependencies": { @@ -6140,13 +6859,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-toolbars": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.6.10.tgz", - "integrity": "sha512-cHhI+9r/Wt/l+E02V2UvybkmdembqjVUagLNHRIRQSqx0tH762G0OD3JzOC2nqmXMjABY2mUkADORhWERfMPjg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.6.14.tgz", + "integrity": "sha512-W/wEXT8h3VyZTVfWK/84BAcjAxTdtRiAkT2KAN0nbSHxxB5KEM1MjKpKu2upyzzMa3EywITqbfy4dP6lpkVTwQ==", "dev": true, "license": "MIT", "funding": { @@ -6154,13 +6873,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/addon-viewport": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.6.10.tgz", - "integrity": "sha512-0ATxfA+bHpTcdTUc83VVJF3XPJqe64Yl1I9UWnx/XG2gzo8avRA44pQe8ETH5Fwr7kAvDMqW6LXAisfsl20wrg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.6.14.tgz", + "integrity": "sha512-gNzVQbMqRC+/4uQTPI2ZrWuRHGquTMZpdgB9DrD88VTEjNudP+J6r8myLfr2VvGksBbUMHkGHMXHuIhrBEnXYA==", "dev": true, "license": "MIT", "dependencies": { @@ -6171,13 +6890,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/blocks": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.6.10.tgz", - "integrity": "sha512-S9XVyN36utNAo78/IHUP1DpCw7vBw5Ef4iO9diF+MLtxP3jJwFXPFkyBSi7AnWig9FH3I8vYI1fh1a4/nk1H4g==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.6.14.tgz", + "integrity": "sha512-rBMHAfA39AGHgkrDze4RmsnQTMw1ND5fGWobr9pDcJdnDKWQWNRD7Nrlxj0gFlN3n4D9lEZhWGdFrCbku7FVAQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6191,7 +6910,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^8.6.10" + "storybook": "^8.6.14" }, "peerDependenciesMeta": { "react": { @@ -6203,13 +6922,13 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.6.10.tgz", - "integrity": "sha512-RXT4uflQSgXSHbWG+Z2Im5r7Ji1wj0Lyo6hVJZIBLEbaIbjfvPtP9CXlhK/z1h90cegHTnkYDd01RHwgmlKRrg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-8.6.14.tgz", + "integrity": "sha512-ajWYhy32ksBWxwWHrjwZzyC0Ii5ZTeu5lsqA95Q/EQBB0P5qWlHWGM3AVyv82Mz/ND03ebGy123uVwgf6olnYQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "8.6.10", + "@storybook/csf-plugin": "8.6.14", "browser-assert": "^1.2.1", "ts-dedent": "^2.0.0" }, @@ -6218,14 +6937,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10", + "storybook": "^8.6.14", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" } }, "node_modules/@storybook/components": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.6.10.tgz", - "integrity": "sha512-9TE2aZU+1zjGO4R74jc4Dmx+pFb+9hm1vnlWH+WVfYV1nCSCZOMmMoO2J86PHPkR6RmPjcQJXz4ySdBbYiwKiw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.6.14.tgz", + "integrity": "sha512-HNR2mC5I4Z5ek8kTrVZlIY/B8gJGs5b3XdZPBPBopTIN6U/YHXiDyOjY3JlaS4fSG1fVhp/Qp1TpMn1w/9m1pw==", "dev": true, "license": "MIT", "funding": { @@ -6237,13 +6956,13 @@ } }, "node_modules/@storybook/core": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.10.tgz", - "integrity": "sha512-VyhE/9/idPeeObsx+DyD8RR2iEwLJGL9rYz61r+1IrpndIVnlYD+vjxc0Y/1jTG1RvShWzEF2A/vzsJ9PzXqcw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.14.tgz", + "integrity": "sha512-1P/w4FSNRqP8j3JQBOi3yGt8PVOgSRbP66Ok520T78eJBeqx9ukCfl912PQZ7SPbW3TIunBwLXMZOjZwBB/JmA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/theming": "8.6.10", + "@storybook/theming": "8.6.14", "better-opn": "^3.0.2", "browser-assert": "^1.2.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", @@ -6279,9 +6998,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.10.tgz", - "integrity": "sha512-yjtFyjEvmmWUG1NzM81/CLI5rOUG311EoPmRnvbNpdzaVug4emC3rX9mR69DsrXfL7kLTDltDH8tjA7wLxpGMA==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.14.tgz", + "integrity": "sha512-dErtc9teAuN+eelN8FojzFE635xlq9cNGGGEu0WEmMUQ4iJ8pingvBO1N8X3scz4Ry7KnxX++NNf3J3gpxS8qQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6292,7 +7011,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/global": { @@ -6316,9 +7035,9 @@ } }, "node_modules/@storybook/instrumenter": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.6.10.tgz", - "integrity": "sha512-Hlps6V0lkhFMbgcJQRynVBne51ciG7Xv+YtiDCd0PQtvZu8+vVJr/ebWt3nCwpvkRHooYiud1ScA3K8McyA73w==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.6.14.tgz", + "integrity": "sha512-iG4MlWCcz1L7Yu8AwgsnfVAmMbvyRSk700Mfy2g4c8y5O+Cv1ejshE1LBBsCwHgkuqU0H4R0qu4g23+6UnUemQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6330,13 +7049,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/manager-api": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.6.10.tgz", - "integrity": "sha512-roJ2aXqbZfSh9IM4q34U2GpU0CDmUjTKGwAnYOS2SG6rGLPenPflIksW8A52cVFdzGa4eH/KzP2FIg5Zi1KLJQ==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.6.14.tgz", + "integrity": "sha512-ez0Zihuy17udLbfHZQXkGqwtep0mSGgHcNzGN7iZrMP1m+VmNo+7aGCJJdvXi7+iU3yq8weXSQFWg5DqWgLS7g==", "dev": true, "license": "MIT", "funding": { @@ -6348,9 +7067,9 @@ } }, "node_modules/@storybook/preview-api": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.6.10.tgz", - "integrity": "sha512-8ki1GgiUlcSqZD3Oe42Fy0uW3E7XPpMAyzO+NSnHCKKfNlZgi036Rr+FyGcKwG5lJyubWwNesPGQX5UHigYu4w==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.6.14.tgz", + "integrity": "sha512-2GhcCd4dNMrnD7eooEfvbfL4I83qAqEyO0CO7JQAmIO6Rxb9BsOLLI/GD5HkvQB73ArTJ+PT50rfaO820IExOQ==", "dev": true, "license": "MIT", "funding": { @@ -6362,18 +7081,18 @@ } }, "node_modules/@storybook/react": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.6.10.tgz", - "integrity": "sha512-QCs5nyXe+G2ZoZ1uspEsnSp7VYLJej5dJ1bSf22rrdHQde641zvC2HZQaba4dqR2YQxRCG9JtCdaQ3UVmnfzzA==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.6.14.tgz", + "integrity": "sha512-BOepx5bBFwl/CPI+F+LnmMmsG1wQYmrX/UQXgUbHQUU9Tj7E2ndTnNbpIuSLc8IrM03ru+DfwSg1Co3cxWtT+g==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.6.10", + "@storybook/components": "8.6.14", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.6.10", - "@storybook/preview-api": "8.6.10", - "@storybook/react-dom-shim": "8.6.10", - "@storybook/theming": "8.6.10" + "@storybook/manager-api": "8.6.14", + "@storybook/preview-api": "8.6.14", + "@storybook/react-dom-shim": "8.6.14", + "@storybook/theming": "8.6.14" }, "engines": { "node": ">=18.0.0" @@ -6383,10 +7102,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.6.10", + "@storybook/test": "8.6.14", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.10", + "storybook": "^8.6.14", "typescript": ">= 4.2.x" }, "peerDependenciesMeta": { @@ -6399,9 +7118,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.6.10.tgz", - "integrity": "sha512-r4Q5stsoIlSEvOpOJgyFGPej+t9uuIzGI2ul83XNtiHEBs7xlmUN7qAm+U9cOuNZ7mPOXDKt9nZfUfCP5Ouhyw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.6.14.tgz", + "integrity": "sha512-0hixr3dOy3f3M+HBofp3jtMQMS+sqzjKNgl7Arfuj3fvjmyXOks/yGjDImySR4imPtEllvPZfhiQNlejheaInw==", "dev": true, "license": "MIT", "funding": { @@ -6411,20 +7130,20 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/react-vite": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.6.10.tgz", - "integrity": "sha512-CyptES7yE1fnZWMN5xk6AFPuchjg4YN8VvBaC0YyveKhfeXlczGwG9nPU28ZY3I+Xzz5g/A6rBvgSRQ88hc/bQ==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-8.6.14.tgz", + "integrity": "sha512-FZU0xMPxa4/TO87FgcWwappOxLBHZV5HSRK5K+2bJD7rFJAoNorbHvB4Q1zvIAk7eCMjkr2GPCPHx9PRB9vJFg==", "dev": true, "license": "MIT", "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "0.5.0", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "8.6.10", - "@storybook/react": "8.6.10", + "@storybook/builder-vite": "8.6.14", + "@storybook/react": "8.6.14", "find-up": "^5.0.0", "magic-string": "^0.30.0", "react-docgen": "^7.0.0", @@ -6439,10 +7158,10 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/test": "8.6.10", + "@storybook/test": "8.6.14", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.10", + "storybook": "^8.6.14", "vite": "^4.0.0 || ^5.0.0 || ^6.0.0" }, "peerDependenciesMeta": { @@ -6475,14 +7194,14 @@ } }, "node_modules/@storybook/test": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.6.10.tgz", - "integrity": "sha512-eCQueRB0SpwjnXcE5wmUNu2G7Z7nRzzHw+0QW2Yc3DoA5UwmOiuwTseZenQkD019dwvFJ87fHo1xXnDAamX1Tg==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.6.14.tgz", + "integrity": "sha512-GkPNBbbZmz+XRdrhMtkxPotCLOQ1BaGNp/gFZYdGDk2KmUWBKmvc5JxxOhtoXM2703IzNFlQHSSNnhrDZYuLlw==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.6.10", + "@storybook/instrumenter": "8.6.14", "@testing-library/dom": "10.4.0", "@testing-library/jest-dom": "6.5.0", "@testing-library/user-event": "14.5.2", @@ -6494,7 +7213,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.10" + "storybook": "^8.6.14" } }, "node_modules/@storybook/test/node_modules/@testing-library/jest-dom": { @@ -6518,12 +7237,26 @@ "yarn": ">=1" } }, - "node_modules/@storybook/test/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", + "node_modules/@storybook/test/node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@storybook/test/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6579,9 +7312,9 @@ } }, "node_modules/@storybook/theming": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.10.tgz", - "integrity": "sha512-4E5ArcJ/bhrWtlYzQDbtA3O3pha/Ys0Ja6X4waJQ5UJENzUMdVz6vTLSUHtG5hNRmSqreogxe4Ed88+0JtY7NQ==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.14.tgz", + "integrity": "sha512-r4y+LsiB37V5hzpQo+BM10PaCsp7YlZ0YcZzQP1OCkPlYXmUAFy2VvDKaFRpD8IeNPKug2u4iFm/laDEbs03dg==", "dev": true, "license": "MIT", "funding": { @@ -6739,9 +7472,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.1.0.tgz", - "integrity": "sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", "dev": true, "license": "MIT", "dependencies": { @@ -6767,10 +7500,11 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12", "npm": ">=6" @@ -7199,10 +7933,11 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/expect": { "version": "1.20.4", @@ -7393,13 +8128,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", - "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", + "version": "22.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", + "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-json": { @@ -7414,6 +8149,26 @@ "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", "dev": true }, + "node_modules/@types/postcss-modules-local-by-default": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.2.tgz", + "integrity": "sha512-CtYCcD+L+trB3reJPny+bKWKMzPfxEyQpKIwit7kErnOexf5/faaGpkFy4I5AwbV4hp1sk7/aTg0tt0B67VkLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@types/postcss-modules-scope": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.4.tgz", + "integrity": "sha512-//ygSisVq9kVI0sqx3UPLzWIMCmtSVrzdljtuaAEJtGoGnpjBikZ2sXO5MpH9SnWX9HRfXxHifDAXcQjupWnIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss": "^8.0.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -7565,6 +8320,12 @@ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, "node_modules/@types/undertaker": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.11.tgz", @@ -7648,105 +8409,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz", - "integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/type-utils": "8.28.0", - "@typescript-eslint/utils": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", - "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", - "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", - "integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/typescript-estree": "8.28.0" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7756,78 +8433,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { + "@typescript-eslint/parser": "^8.39.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", - "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">= 4" } }, "node_modules/@typescript-eslint/experimental-utils": { @@ -7972,16 +8590,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz", - "integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/typescript-estree": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4" }, "engines": { @@ -7993,56 +8611,19 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", - "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", - "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", + "debug": "^4.3.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8055,15 +8636,15 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", - "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8073,86 +8654,35 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/parser/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/@typescript-eslint/parser/node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz", - "integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.28.0", - "@typescript-eslint/utils": "8.28.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8163,31 +8693,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", - "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", - "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", + "node_modules/@typescript-eslint/types": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", "dev": true, "license": "MIT", "engines": { @@ -8198,21 +8710,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", - "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/visitor-keys": "8.28.0", + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8222,20 +8736,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", - "integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.28.0", - "@typescript-eslint/types": "8.28.0", - "@typescript-eslint/typescript-estree": "8.28.0" + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", + "debug": "^4.3.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8245,52 +8758,37 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", - "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.28.0", - "eslint-visitor-keys": "^4.2.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", @@ -8306,121 +8804,59 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/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, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@typescript-eslint/types": "8.39.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, + "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/eslint" } }, "node_modules/@ungap/structured-clone": { @@ -8525,6 +8961,28 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vscode-elements/elements": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@vscode-elements/elements/-/elements-1.14.0.tgz", + "integrity": "sha512-fUOP8O/Pwy8zbD8hGSy1plBg/764hdM9jIMu8uG7GQJOrOB+uQ/ystYxkiUcN6P7OBHvqkBKO1j6vDrkaOJg6Q==", + "license": "MIT", + "dependencies": { + "lit": "^3.2.1" + } + }, + "node_modules/@vscode-elements/react-elements": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@vscode-elements/react-elements/-/react-elements-0.9.0.tgz", + "integrity": "sha512-pGWp6OBDAZXJ0tZqN+2SCiKhvhW3/cE4XJyiVHXH4Ft6KteuNVg20oexFv0M66U9iAZElQjPF8M9pBBABLaUZg==", + "license": "ISC", + "dependencies": { + "@lit/react": "^1.0.6", + "@vscode-elements/elements": "^1.13.0" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@vscode/codicons": { "version": "0.0.36", "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.36.tgz", @@ -8753,20 +9211,6 @@ "win32" ] }, - "node_modules/@vscode/webview-ui-toolkit": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.4.0.tgz", - "integrity": "sha512-modXVHQkZLsxgmd5yoP3ptRC/G8NBDD+ob+ngPiWNQdlrH6H1xR/qgOBD85bfU3BhOB5sZzFWBwwhp9/SfoHww==", - "dependencies": { - "@microsoft/fast-element": "^1.12.0", - "@microsoft/fast-foundation": "^2.49.4", - "@microsoft/fast-react-wrapper": "^0.3.22", - "tslib": "^2.6.2" - }, - "peerDependencies": { - "react": ">=16.9.0" - } - }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -8774,9 +9218,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -9026,9 +9470,10 @@ } }, "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -9339,7 +9784,8 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/async": { "version": "3.2.5", @@ -9459,12 +9905,13 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "dependencies": { - "dequal": "^2.0.3" + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" } }, "node_modules/azure-devops-node-api": { @@ -9758,16 +10205,6 @@ "node": ">=12.0.0" } }, - "node_modules/better-opn/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/better-opn/node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -9786,15 +10223,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -9853,23 +10281,12 @@ "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9893,9 +10310,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -9911,11 +10328,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -10002,21 +10420,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -10103,9 +10506,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001611", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz", - "integrity": "sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -10120,7 +10523,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chai": { "version": "5.2.0", @@ -10636,6 +11040,19 @@ "node": ">= 0.6" } }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/copy-props": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", @@ -10907,6 +11324,19 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cssstyle": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", @@ -11329,7 +11759,8 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/data-urls": { "version": "5.0.0", @@ -11500,156 +11931,6 @@ "node": ">=0.10.0" } }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -11668,15 +11949,13 @@ } }, "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/define-properties": { @@ -11907,6 +12186,19 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -11987,10 +12279,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.742", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.742.tgz", - "integrity": "sha512-EhE+z1d5RNytAq/qnGAxPR+ie3UzKbv7qqQc0wnEbOh+KDUplgfzkGSCy9d78B+S+nVNTS42BabHXB6Ni+Ud4w==", - "dev": true + "version": "1.5.194", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz", + "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==", + "dev": true, + "license": "ISC" }, "node_modules/emitter-listener": { "version": "1.1.2", @@ -12072,6 +12365,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -12302,9 +12609,10 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12319,58 +12627,64 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -12481,28 +12795,14 @@ "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-deprecation": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-deprecation/-/eslint-plugin-deprecation-3.0.0.tgz", - "integrity": "sha512-JuVLdNg/uf0Adjg2tpTyYoYaMbwQNn/c78P1HcccokvhtRphgnRjZDKmhlxbxYptppex03zO76f97DD/yQHv7A==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^7.0.0", - "ts-api-utils": "^1.3.0", - "tslib": "^2.3.1" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "typescript": "^4.2.4 || ^5.0.0" - } - }, "node_modules/eslint-plugin-escompat": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", - "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==", + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.11.4.tgz", + "integrity": "sha512-j0ywwNnIufshOzgAu+PfIig1c7VRClKSNKzpniMT2vXQ4leL5q+e/SpMFQU0nrdL2WFFM44XmhSuwmxb3G0CJg==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.21.0" + "browserslist": "^4.23.1" }, "peerDependencies": { "eslint": ">=5.14.1" @@ -12561,122 +12861,54 @@ } }, "node_modules/eslint-plugin-github": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-5.0.1.tgz", - "integrity": "sha512-qbXG3wL5Uh2JB92EKeX2hPtO9c/t75qVxQjVLYuTFfhHifLZzv9CBvLCvoaBhLrAC/xTMVht7DK/NofYK8X4Dg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-6.0.0.tgz", + "integrity": "sha512-J8MvUoiR/TU/Y9NnEmg1AnbvMUj9R6IO260z47zymMLLvso7B4c80IKjd8diqmqtSmeXXlbIus4i0SvK84flag==", "dev": true, + "license": "MIT", "dependencies": { + "@eslint/compat": "^1.2.3", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.14.0", "@github/browserslist-config": "^1.0.0", - "@typescript-eslint/eslint-plugin": "^7.0.1", - "@typescript-eslint/parser": "^7.0.1", + "@typescript-eslint/eslint-plugin": "^8.0.0", + "@typescript-eslint/parser": "^8.0.0", "aria-query": "^5.3.0", "eslint-config-prettier": ">=8.0.0", - "eslint-plugin-escompat": "^3.3.3", + "eslint-plugin-escompat": "^3.11.3", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-filenames": "^1.3.2", "eslint-plugin-i18n-text": "^1.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-no-only-tests": "^3.0.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-prettier": "^5.2.1", "eslint-rule-documentation": ">=1.0.0", + "globals": "^16.0.0", "jsx-ast-utils": "^3.3.2", "prettier": "^3.0.0", - "svg-element-attributes": "^1.3.1" + "svg-element-attributes": "^1.3.1", + "typescript": "^5.7.3", + "typescript-eslint": "^8.14.0" }, "bin": { "eslint-ignore-errors": "bin/eslint-ignore-errors.js" }, "peerDependencies": { - "eslint": "^8.0.1" - } - }, - "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8 || ^9" } }, - "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "node_modules/eslint-plugin-github/node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint-plugin-i18n-text": { @@ -12777,42 +13009,43 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" }, "engines": { "node": ">=4.0" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=4" + "node": ">= 0.4" } }, "node_modules/eslint-plugin-no-only-tests": { @@ -12825,13 +13058,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz", + "integrity": "sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==", "dev": true, + "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.11.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -12842,7 +13076,7 @@ "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", - "eslint-config-prettier": "*", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -12888,15 +13122,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -12938,152 +13173,20 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.8.0.tgz", - "integrity": "sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==", - "dev": true, - "dependencies": { - "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.62.0", - "requireindex": "^1.2.0", - "ts-dedent": "^2.2.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "eslint": ">=6" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@storybook/csf": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", - "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.1.tgz", + "integrity": "sha512-g4/i9yW6cl4TCEMzYyALNvO3d/jB6TDvSs/Pmye7dHDrra2B7dgZJGzmEWILD62brVrLVHNoXgy2dNPtx80kmw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@typescript-eslint/utils": "^8.8.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=20.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-plugin-storybook/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "eslint": ">=8", + "storybook": "^9.1.1" } }, "node_modules/eslint-rule-documentation": { @@ -13096,16 +13199,17 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -13182,31 +13286,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10.13.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10.13.0" } }, "node_modules/eslint/node_modules/has-flag": { @@ -13236,31 +13338,32 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -13382,11 +13485,6 @@ "dev": true, "license": "ISC" }, - "node_modules/exenv-es6": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz", - "integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==" - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -13558,16 +13656,32 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/filelist": { @@ -13580,10 +13694,11 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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" } @@ -13677,24 +13792,25 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/flush-write-stream": { "version": "1.1.1", @@ -13752,15 +13868,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -14115,10 +14232,11 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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" } @@ -14928,10 +15046,11 @@ } }, "node_modules/husky": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", - "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, + "license": "MIT", "bin": { "husky": "bin.js" }, @@ -14953,6 +15072,19 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -14982,12 +15114,33 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, + "node_modules/immutable": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz", + "integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==", + "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", @@ -15446,39 +15599,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container/node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -15754,6 +15874,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true, + "license": "MIT" + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -18374,9 +18501,9 @@ } }, "node_modules/jest/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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": { @@ -19357,7 +19484,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -19585,6 +19713,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -19607,17 +19736,26 @@ "node": ">=6" } }, + "node_modules/koffi": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.12.0.tgz", + "integrity": "sha512-J886y/bvoGG4ZhMVstB2Nh6/q9tzAYn0kaH7Ss8DWavGIxP5jOLzUY9IZzw9pMuXArj0SLSpl0MYsKRURPAv7g==", + "hasInstallScript": true, + "license": "MIT" + }, "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, + "license": "MIT", "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -19657,6 +19795,44 @@ "node": ">= 0.10" } }, + "node_modules/less": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.3.0.tgz", + "integrity": "sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -20003,6 +20179,37 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/lit": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz", + "integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.1.0", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-element": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz", + "integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.2.0" + } + }, + "node_modules/lit-html": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz", + "integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -20340,6 +20547,43 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -21254,9 +21498,9 @@ "dev": true }, "node_modules/msw": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.6.8.tgz", - "integrity": "sha512-nxXxnH6WALZ9a7rsQp4HU2AaD4iGAiouMmE/MY4al7pXTibgA6OZOuKhmN2WBIM6w9qMKwRtX8p2iOb45B2M/Q==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.4.tgz", + "integrity": "sha512-A2kuMopOjAjNEYkn0AnB1uj+x7oBjLIunFk7Ud4icEnVWFf6iBekn8oXW4zIwcpfEdWP9sLqyVaHVzneWoGEww==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -21269,12 +21513,12 @@ "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", "@types/statuses": "^2.0.4", - "chalk": "^4.1.2", "graphql": "^16.8.1", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", "strict-event-emitter": "^0.5.1", "type-fest": "^4.26.1", "yargs": "^17.7.2" @@ -21297,59 +21541,11 @@ } } }, - "node_modules/msw/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/msw/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/msw/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "license": "MIT" - }, - "node_modules/msw/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" }, "node_modules/msw/node_modules/type-fest": { "version": "4.30.0", @@ -21409,6 +21605,24 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -21442,10 +21656,11 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "2.5.0", @@ -21984,6 +22199,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -22534,6 +22759,117 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -22595,10 +22931,11 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.1.tgz", + "integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -22709,6 +23046,14 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -22989,9 +23334,10 @@ } }, "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -23308,6 +23654,13 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reserved-words": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", + "integrity": "sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -23486,13 +23839,13 @@ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" }, "node_modules/rollup": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", - "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", + "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.7" }, "bin": { "rollup": "dist/bin/rollup" @@ -23502,25 +23855,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.8", - "@rollup/rollup-android-arm64": "4.34.8", - "@rollup/rollup-darwin-arm64": "4.34.8", - "@rollup/rollup-darwin-x64": "4.34.8", - "@rollup/rollup-freebsd-arm64": "4.34.8", - "@rollup/rollup-freebsd-x64": "4.34.8", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", - "@rollup/rollup-linux-arm-musleabihf": "4.34.8", - "@rollup/rollup-linux-arm64-gnu": "4.34.8", - "@rollup/rollup-linux-arm64-musl": "4.34.8", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", - "@rollup/rollup-linux-riscv64-gnu": "4.34.8", - "@rollup/rollup-linux-s390x-gnu": "4.34.8", - "@rollup/rollup-linux-x64-gnu": "4.34.8", - "@rollup/rollup-linux-x64-musl": "4.34.8", - "@rollup/rollup-win32-arm64-msvc": "4.34.8", - "@rollup/rollup-win32-ia32-msvc": "4.34.8", - "@rollup/rollup-win32-x64-msvc": "4.34.8", + "@rollup/rollup-android-arm-eabi": "4.40.1", + "@rollup/rollup-android-arm64": "4.40.1", + "@rollup/rollup-darwin-arm64": "4.40.1", + "@rollup/rollup-darwin-x64": "4.40.1", + "@rollup/rollup-freebsd-arm64": "4.40.1", + "@rollup/rollup-freebsd-x64": "4.40.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", + "@rollup/rollup-linux-arm-musleabihf": "4.40.1", + "@rollup/rollup-linux-arm64-gnu": "4.40.1", + "@rollup/rollup-linux-arm64-musl": "4.40.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", + "@rollup/rollup-linux-riscv64-gnu": "4.40.1", + "@rollup/rollup-linux-riscv64-musl": "4.40.1", + "@rollup/rollup-linux-s390x-gnu": "4.40.1", + "@rollup/rollup-linux-x64-gnu": "4.40.1", + "@rollup/rollup-linux-x64-musl": "4.40.1", + "@rollup/rollup-win32-arm64-msvc": "4.40.1", + "@rollup/rollup-win32-ia32-msvc": "4.40.1", + "@rollup/rollup-win32-x64-msvc": "4.40.1", "fsevents": "~2.3.2" } }, @@ -23531,21 +23885,6 @@ "dev": true, "license": "MIT" }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -23643,6 +23982,57 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sass": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.3.tgz", + "integrity": "sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/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/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", @@ -23670,9 +24060,10 @@ } }, "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -24014,13 +24405,13 @@ } }, "node_modules/storybook": { - "version": "8.6.10", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.10.tgz", - "integrity": "sha512-7LUD9hNllMZZhDJutxgejrpWI89rsBF+p2kCfWoJ9EuBTgRy8bbVQZlG7bE1gf7qLmnabnklsFslTakS2SbKRw==", + "version": "8.6.14", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.14.tgz", + "integrity": "sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.6.10" + "@storybook/core": "8.6.14" }, "bin": { "getstorybook": "bin/index.cjs", @@ -24192,6 +24583,21 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -24440,6 +24846,58 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" }, + "node_modules/stylus": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.62.0.tgz", + "integrity": "sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "~4.3.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.3.0", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus/node_modules/@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylus/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -24534,25 +24992,41 @@ "dev": true }, "node_modules/synckit": { - "version": "0.8.6", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz", - "integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", + "integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==", "dev": true, + "license": "MIT", "dependencies": { - "@pkgr/utils": "^2.4.2", - "tslib": "^2.6.2" + "@pkgr/core": "^0.2.3", + "tslib": "^2.8.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/synckit/node_modules/@pkgr/core": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", + "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" } }, - "node_modules/tabbable": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", - "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" + "node_modules/synckit/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/tapable": { "version": "2.2.1", @@ -24716,7 +25190,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/textextensions": { "version": "3.3.0", @@ -24780,6 +25255,36 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinyrainbow": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", @@ -24800,18 +25305,6 @@ "node": ">=14.0.0" } }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tldts": { "version": "6.1.85", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", @@ -24833,9 +25326,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", "engines": { "node": ">=14.14" @@ -24946,15 +25439,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-dedent": { @@ -24967,10 +25461,11 @@ } }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.3.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.2.tgz", + "integrity": "sha512-bJJkrWc6PjFVz5g2DGCNUo8z7oFEYaz1xP1NpeDU7KNLMWPpEyV8Chbpkn8xjzgRDpQhnGMyvyldoL7h8JXyug==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", @@ -24979,7 +25474,8 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.1", + "type-fest": "^4.39.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -25014,6 +25510,19 @@ } } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", + "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-json-schema-generator": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", @@ -25037,10 +25546,11 @@ } }, "node_modules/ts-json-schema-generator/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "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" } @@ -25465,10 +25975,11 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -25477,6 +25988,315 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.38.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.38.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/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/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/typescript-eslint/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/typescript-plugin-css-modules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.1.0.tgz", + "integrity": "sha512-6h+sLBa4l+XYSTn/31vZHd/1c3SvAbLpobY6FxDiUOHJQG1eD9Gh3eCs12+Eqc+TCOAdxcO+zAPvUq0jBfdciw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/postcss-modules-local-by-default": "^4.0.2", + "@types/postcss-modules-scope": "^3.0.4", + "dotenv": "^16.4.2", + "icss-utils": "^5.1.0", + "less": "^4.2.0", + "lodash.camelcase": "^4.3.0", + "postcss": "^8.4.35", + "postcss-load-config": "^3.1.4", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.4", + "postcss-modules-scope": "^3.1.1", + "reserved-words": "^0.1.2", + "sass": "^1.70.0", + "source-map-js": "^1.0.2", + "stylus": "^0.62.0", + "tsconfig-paths": "^4.2.0" + }, + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/typescript-plugin-css-modules/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript-plugin-css-modules/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -25549,10 +26369,11 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -25645,19 +26466,10 @@ "node": ">=14.0.0" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "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": [ { @@ -25673,9 +26485,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -25998,15 +26811,18 @@ } }, "node_modules/vite": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz", - "integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", + "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -26092,6 +26908,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vscode-extension-telemetry": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz", @@ -26161,9 +26990,10 @@ } }, "node_modules/vscode-languageclient/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -26710,9 +27540,10 @@ } }, "node_modules/zip-a-folder/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3615e186750..863176b2067 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.17.3", + "version": "1.17.5", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", @@ -14,7 +14,7 @@ }, "engines": { "vscode": "^1.90.0", - "node": "^20.18.2", + "node": "^22.17.0", "npm": ">=7.20.6" }, "categories": [ @@ -571,6 +571,10 @@ "command": "codeQL.runQueries", "title": "CodeQL: Run Queries in Selected Files" }, + { + "command": "codeQL.runQuerySuite", + "title": "CodeQL: Run Selected Query Suite" + }, { "command": "codeQL.quickEval", "title": "CodeQL: Quick Evaluation" @@ -786,6 +790,10 @@ "command": "codeQL.trimCache", "title": "CodeQL: Trim Cache" }, + { + "command": "codeQL.trimOverlayBaseCache", + "title": "CodeQL: Trim Overlay Base Cache" + }, { "command": "codeQL.installPackDependencies", "title": "CodeQL: Install Pack Dependencies" @@ -954,6 +962,10 @@ "command": "codeQLQueryHistory.copyRepoList", "title": "Copy Repository List" }, + { + "command": "codeQLQueryHistory.viewAutofixes", + "title": "View Autofixes" + }, { "command": "codeQLQueryResults.down", "title": "CodeQL: Navigate Down in Local Result Viewer" @@ -1292,6 +1304,11 @@ "group": "1_queryHistory@1", "when": "viewItem == remoteResultsItem" }, + { + "command": "codeQLQueryHistory.viewAutofixes", + "group": "1_queryHistory@2", + "when": "viewItem == remoteResultsItem && config.codeQL.canary" + }, { "command": "codeQLQueries.runLocalQueryFromQueriesPanel", "group": "inline", @@ -1361,6 +1378,11 @@ "group": "9_qlCommands", "when": "resourceScheme != codeql-zip-archive" }, + { + "command": "codeQL.runQuerySuite", + "group": "9_qlCommands", + "when": "resourceScheme != codeql-zip-archive && resourceExtname == .qls && !explorerResourceIsFolder && !listMultiSelection && config.codeQL.canary" + }, { "command": "codeQL.runVariantAnalysisContextExplorer", "group": "9_qlCommands", @@ -1458,6 +1480,10 @@ "command": "codeQL.runQueries", "when": "false" }, + { + "command": "codeQL.runQuerySuite", + "when": "false" + }, { "command": "codeQL.quickEval", "when": "editorLangId == ql" @@ -1693,6 +1719,10 @@ "command": "codeQLQueryHistory.copyRepoList", "when": "false" }, + { + "command": "codeQLQueryHistory.viewAutofixes", + "when": "false" + }, { "command": "codeQLQueryHistory.showQueryText", "when": "false" @@ -1791,6 +1821,10 @@ }, { "command": "codeQL.trimCache" + }, + { + "command": "codeQL.trimOverlayBaseCache", + "when": "codeQL.cliFeatures.queryServerTrimCacheWithMode" } ], "editor/context": [ @@ -1951,9 +1985,9 @@ "test:cli-integration": "jest --projects test/vscode-tests/cli-integration --verbose", "clean-test-dir": "find . -type d -name .vscode-test -exec rm -r {} +", "update-vscode": "node ./node_modules/vscode/bin/install", - "format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix", - "lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0", - "lint-ci": "SARIF_ESLINT_IGNORE_SUPPRESSED=true eslint . --ext .js,.ts,.tsx --max-warnings=0 --format @microsoft/eslint-formatter-sarif --output-file=build/eslint.sarif", + "format": "prettier --write **/*.{ts,tsx} && eslint . --fix", + "lint": "eslint . --max-warnings=0", + "lint-ci": "SARIF_ESLINT_IGNORE_SUPPRESSED=true eslint . --max-warnings=0 --format @microsoft/eslint-formatter-sarif --output-file=build/eslint.sarif", "lint:markdown": "markdownlint-cli2 \"../../**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/.vscode-test/**\" \"!**/build/cli/v*/**\"", "find-deadcode": "vite-node scripts/find-deadcode.ts", "format-staged": "lint-staged", @@ -1972,17 +2006,18 @@ "@octokit/plugin-retry": "^7.2.0", "@octokit/plugin-throttling": "^9.6.0", "@octokit/rest": "^21.1.1", + "@vscode-elements/react-elements": "^0.9.0", "@vscode/codicons": "^0.0.36", "@vscode/debugadapter": "^1.59.0", "@vscode/debugprotocol": "^1.68.0", - "@vscode/webview-ui-toolkit": "^1.0.1", "ajv": "^8.11.0", "chokidar": "^3.6.0", "d3": "^7.9.0", "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", - "msw": "^2.6.8", + "koffi": "^2.12.0", + "msw": "^2.7.4", "nanoid": "^5.0.7", "p-queue": "^8.0.1", "proper-lockfile": "^4.1.2", @@ -1993,7 +2028,7 @@ "source-map-support": "^0.5.21", "stream-json": "^1.7.3", "styled-components": "^6.1.13", - "tmp": "^0.2.1", + "tmp": "^0.2.5", "tmp-promise": "^3.0.2", "tree-kill": "^1.2.2", "vscode-extension-telemetry": "^0.1.6", @@ -2003,34 +2038,35 @@ "zip-a-folder": "^3.1.6" }, "devDependencies": { - "@babel/core": "^7.24.6", + "@babel/core": "^7.28.0", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", + "@eslint/js": "^9.28.0", "@faker-js/faker": "^9.0.3", "@github/markdownlint-github": "^0.6.3", "@jest/environment": "^30.0.0-alpha.7", "@jest/environment-jsdom-abstract": "^30.0.0-alpha.7", "@microsoft/eslint-formatter-sarif": "^3.1.0", "@playwright/test": "^1.50.1", - "@storybook/addon-a11y": "^8.6.10", - "@storybook/addon-actions": "^8.6.10", - "@storybook/addon-essentials": "^8.6.10", - "@storybook/addon-interactions": "^8.6.10", - "@storybook/addon-links": "^8.6.10", + "@storybook/addon-a11y": "^8.6.14", + "@storybook/addon-actions": "^8.6.14", + "@storybook/addon-essentials": "^8.6.14", + "@storybook/addon-interactions": "^8.6.14", + "@storybook/addon-links": "^8.6.14", "@storybook/blocks": "^8.6.0", - "@storybook/components": "^8.6.10", + "@storybook/components": "^8.6.14", "@storybook/csf": "^0.1.13", "@storybook/icons": "^1.4.0", - "@storybook/manager-api": "^8.6.10", - "@storybook/react": "^8.6.10", - "@storybook/react-vite": "^8.6.10", - "@storybook/theming": "^8.6.10", + "@storybook/manager-api": "^8.6.14", + "@storybook/react": "^8.6.14", + "@storybook/react-vite": "^8.6.14", + "@storybook/theming": "^8.6.14", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.1.0", - "@testing-library/user-event": "^14.5.2", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/cross-spawn": "^6.0.6", "@types/d3": "^7.4.0", "@types/d3-graphviz": "^2.6.6", @@ -2040,7 +2076,7 @@ "@types/gulp-replace": "^1.1.0", "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.6", - "@types/node": "20.17.*", + "@types/node": "22.17.*", "@types/proper-lockfile": "^4.1.4", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -2053,8 +2089,8 @@ "@types/tmp": "^0.2.6", "@types/vscode": "1.90.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^8.28.0", - "@typescript-eslint/parser": "^8.28.0", + "@typescript-eslint/eslint-plugin": "^8.39.0", + "@typescript-eslint/parser": "^8.39.0", "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^3.2.1", "ansi-colors": "^4.1.1", @@ -2063,24 +2099,22 @@ "cross-env": "^7.0.3", "cross-spawn": "^7.0.6", "del": "^6.0.0", - "eslint": "^8.56.0", + "eslint": "^9.28.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-deprecation": "^3.0.0", "eslint-plugin-etc": "^2.0.2", - "eslint-plugin-github": "^5.0.1", - "eslint-plugin-import": "^2.31.0", + "eslint-plugin-github": "^6.0.0", "eslint-plugin-jest-dom": "^5.5.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-storybook": "^0.8.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-storybook": "^9.0.12", "glob": "^11.0.1", "gulp": "^5.0.0", "gulp-esbuild": "^0.14.0", "gulp-replace": "^1.1.3", "gulp-typescript": "^5.0.1", - "husky": "^9.1.5", + "husky": "^9.1.7", "jest": "^30.0.0-alpha.7", "jest-runner-vscode": "^3.0.1", "jsdom": "^26.0.0", @@ -2089,16 +2123,17 @@ "markdownlint-cli2-formatter-pretty": "^0.0.7", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", - "prettier": "^3.2.5", - "storybook": "^8.6.10", + "prettier": "^3.6.1", + "storybook": "^8.6.14", "tar-stream": "^3.1.7", "through2": "^4.0.2", - "ts-jest": "^29.2.5", + "ts-jest": "^29.3.2", "ts-json-schema-generator": "^2.3.0", "ts-node": "^10.9.2", "ts-unused-exports": "^10.1.0", "typescript": "^5.6.2", - "vite": "^6.2.4", + "typescript-plugin-css-modules": "^5.1.0", + "vite": "^6.3.4", "vite-node": "^3.0.7" }, "lint-staged": { diff --git a/extensions/ql-vscode/scripts/source-map.ts b/extensions/ql-vscode/scripts/source-map.ts index 551e4dc10a6..f0e6af88923 100644 --- a/extensions/ql-vscode/scripts/source-map.ts +++ b/extensions/ql-vscode/scripts/source-map.ts @@ -131,7 +131,7 @@ async function extractSourceMap() { resolve(sourceMapsDirectory, `${basename(file)}.map`), ); rawSourceMaps.set(file, rawSourceMap); - } catch (e: unknown) { + } catch (e) { // If the file is not found, we will not decode it and not try reading this source map again if (e instanceof Error && "code" in e && e.code === "ENOENT") { rawSourceMaps.set(file, null); diff --git a/extensions/ql-vscode/scripts/update-node-version.ts b/extensions/ql-vscode/scripts/update-node-version.ts index a079356562a..8f412a3a683 100644 --- a/extensions/ql-vscode/scripts/update-node-version.ts +++ b/extensions/ql-vscode/scripts/update-node-version.ts @@ -93,7 +93,7 @@ async function updateNodeVersion() { // If it exists, we can break out of this loop break; - } catch (e: unknown) { + } catch (e) { if (!isExecError(e)) { throw e; } diff --git a/extensions/ql-vscode/src/codeql-cli/cli-version.ts b/extensions/ql-vscode/src/codeql-cli/cli-version.ts index 94f1169c30a..ebd30cbdb62 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli-version.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli-version.ts @@ -10,9 +10,7 @@ interface VersionResult { } export interface CliFeatures { - featuresInVersionResult?: boolean; - mrvaPackCreate?: boolean; - generateSummarySymbolMap?: boolean; + queryServerRunQueries?: boolean; } export interface VersionAndFeatures { diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index a34b6a351ea..1ef1e4a8d40 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -19,6 +19,7 @@ import type { CliConfig } from "../config"; import type { DistributionProvider } from "./distribution"; import { FindDistributionResultKind } from "./distribution"; import { + asError, assertNever, getErrorMessage, getErrorStack, @@ -851,7 +852,7 @@ export class CodeQLCliServer implements Disposable { silent, ).then(resolve, reject); } catch (err) { - reject(err); + reject(asError(err)); } }; // If the server is not running a command, then run the given command immediately, @@ -1191,15 +1192,12 @@ export class CodeQLCliServer implements Disposable { outputPath: string, endSummaryPath: string, ): Promise { - const supportsGenerateSummarySymbolMap = - await this.cliConstraints.supportsGenerateSummarySymbolMap(); const subcommandArgs = [ "--format=text", `--end-summary=${endSummaryPath}`, "--sourcemap", - ...(supportsGenerateSummarySymbolMap - ? ["--summary-symbol-map", "--minify-output"] - : []), + "--summary-symbol-map", + "--minify-output", inputPath, outputPath, ]; @@ -1837,7 +1835,7 @@ export function spawnServer( } let lastStdout: string | Buffer | undefined = undefined; - child.stdout!.on("data", (data) => { + child.stdout.on("data", (data) => { lastStdout = data; }); // Set up event listeners. @@ -1856,9 +1854,9 @@ export function spawnServer( void logger.log(`Last stdout was "${lastStdout.toString()}"`); } }); - child.stderr!.on("data", stderrListener); + child.stderr.on("data", stderrListener); if (stdoutListener !== undefined) { - child.stdout!.on("data", stdoutListener); + child.stdout.on("data", stdoutListener); } if (progressReporter !== undefined) { @@ -1904,17 +1902,13 @@ function shouldDebugCliServer() { export class CliVersionConstraint { // The oldest version of the CLI that we support. This is used to determine // whether to show a warning about the CLI being too old on startup. - public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.18.4"); + public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.19.4"); constructor(private readonly cli: CodeQLCliServer) { /**/ } - async supportsMrvaPackCreate(): Promise { - return (await this.cli.getFeatures()).mrvaPackCreate === true; - } - - async supportsGenerateSummarySymbolMap(): Promise { - return (await this.cli.getFeatures()).generateSummarySymbolMap === true; + async supportsQueryServerRunQueries(): Promise { + return (await this.cli.getFeatures()).queryServerRunQueries === true; } } diff --git a/extensions/ql-vscode/src/codeql-cli/distribution.ts b/extensions/ql-vscode/src/codeql-cli/distribution.ts index 0ec1bede7e4..ba3050ca904 100644 --- a/extensions/ql-vscode/src/codeql-cli/distribution.ts +++ b/extensions/ql-vscode/src/codeql-cli/distribution.ts @@ -336,7 +336,7 @@ class ExtensionSpecificDistributionManager { const distributionStatePath = this.getDistributionStatePath(); try { this.distributionState = await readJson(distributionStatePath); - } catch (e: unknown) { + } catch (e) { if (isIOError(e) && e.code === "ENOENT") { // If the file doesn't exist, that just means we need to create it @@ -346,9 +346,9 @@ class ExtensionSpecificDistributionManager { "distributionFolderIndex", 0, ) ?? 0, - release: (this.extensionContext.globalState.get( - "distributionRelease", - ) ?? null) as Release | null, + release: + this.extensionContext.globalState.get("distributionRelease") ?? + null, }; // This may result in a race condition, but when this happens both processes should write the same file. diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 2fd8a1995d4..64585a8c9e8 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -138,6 +138,7 @@ export type LocalQueryCommands = { "codeQLQueries.createQuery": () => Promise; "codeQL.runLocalQueryFromFileTab": (uri: Uri) => Promise; "codeQL.runQueries": ExplorerSelectionCommandFunction; + "codeQL.runQuerySuite": ExplorerSelectionCommandFunction; "codeQL.quickEval": (uri: Uri) => Promise; "codeQL.quickEvalCount": (uri: Uri) => Promise; "codeQL.quickEvalContextEditor": (uri: Uri) => Promise; @@ -196,6 +197,7 @@ export type QueryHistoryCommands = { "codeQLQueryHistory.itemClicked": TreeViewContextMultiSelectionCommandFunction; "codeQLQueryHistory.openOnGithub": TreeViewContextMultiSelectionCommandFunction; "codeQLQueryHistory.copyRepoList": TreeViewContextMultiSelectionCommandFunction; + "codeQLQueryHistory.viewAutofixes": TreeViewContextMultiSelectionCommandFunction; // Commands in the command palette "codeQL.exportSelectedVariantAnalysisResults": () => Promise; @@ -219,6 +221,7 @@ export type LocalDatabasesCommands = { "codeQL.upgradeCurrentDatabase": () => Promise; "codeQL.clearCache": () => Promise; "codeQL.trimCache": () => Promise; + "codeQL.trimOverlayBaseCache": () => Promise; // Explorer context menu "codeQL.setCurrentDatabase": (uri: Uri) => Promise; diff --git a/extensions/ql-vscode/src/common/helpers-pure.ts b/extensions/ql-vscode/src/common/helpers-pure.ts index 20c1a780cb3..861e250b93f 100644 --- a/extensions/ql-vscode/src/common/helpers-pure.ts +++ b/extensions/ql-vscode/src/common/helpers-pure.ts @@ -69,7 +69,7 @@ export function getErrorMessage(e: unknown): string { } export function getErrorStack(e: unknown): string { - return e instanceof Error ? e.stack ?? "" : ""; + return e instanceof Error ? (e.stack ?? "") : ""; } export function asError(e: unknown): Error { diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 2a0fb24c811..eca0779966a 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -163,6 +163,25 @@ interface SetUserSettingsMsg { userSettings: UserSettings; } +export interface VariantAnalysisUserSettings { + /** Whether to display the "View Autofixes" button. */ + shouldShowViewAutofixesButton: boolean; +} + +export const DEFAULT_VARIANT_ANALYSIS_USER_SETTINGS: VariantAnalysisUserSettings = + { + shouldShowViewAutofixesButton: false, + }; + +/** + * Message indicating that the user's variant analysis configuration + * settings have changed. + */ +interface SetVariantAnalysisUserSettingsMsg { + t: "setVariantAnalysisUserSettings"; + variantAnalysisUserSettings: VariantAnalysisUserSettings; +} + /** * Message indicating that the results view should display interpreted * results. @@ -527,6 +546,11 @@ interface OpenQueryTextMessage { t: "openQueryText"; } +interface ViewAutofixesMessage { + t: "viewAutofixes"; + filterSort?: RepositoriesFilterSortStateWithIds; +} + interface CopyRepositoryListMessage { t: "copyRepositoryList"; filterSort?: RepositoriesFilterSortStateWithIds; @@ -554,13 +578,15 @@ export type ToVariantAnalysisMessage = | SetVariantAnalysisMessage | SetFilterSortStateMessage | SetRepoResultsMessage - | SetRepoStatesMessage; + | SetRepoStatesMessage + | SetVariantAnalysisUserSettingsMsg; export type FromVariantAnalysisMessage = | CommonFromViewMessages | RequestRepositoryResultsMessage | OpenQueryFileMessage | OpenQueryTextMessage + | ViewAutofixesMessage | CopyRepositoryListMessage | ExportResultsMessage | OpenLogsMessage diff --git a/extensions/ql-vscode/src/common/jsonl-reader.ts b/extensions/ql-vscode/src/common/jsonl-reader.ts index 1bf1dc9b897..cff0dcf0ec2 100644 --- a/extensions/ql-vscode/src/common/jsonl-reader.ts +++ b/extensions/ql-vscode/src/common/jsonl-reader.ts @@ -1,6 +1,7 @@ import { stat } from "fs/promises"; import { createReadStream } from "fs-extra"; import type { BaseLogger } from "./logging"; +import { asError } from "./helpers-pure"; const doubleLineBreakRegexp = /\n\r?\n/; @@ -41,7 +42,7 @@ export async function readJsonlFile( stream.resume(); } catch (e) { stream.destroy(); - reject(e); + reject(asError(e)); } } }); @@ -53,7 +54,7 @@ export async function readJsonlFile( void logger?.log(`Finished parsing ${path}`); resolve(); } catch (e) { - reject(e); + reject(asError(e)); } }); stream.on("error", reject); diff --git a/extensions/ql-vscode/src/common/mock-gh-api/request-handlers.ts b/extensions/ql-vscode/src/common/mock-gh-api/request-handlers.ts index 2a1a18bce55..b6d3dc8b403 100644 --- a/extensions/ql-vscode/src/common/mock-gh-api/request-handlers.ts +++ b/extensions/ql-vscode/src/common/mock-gh-api/request-handlers.ts @@ -71,9 +71,10 @@ async function readRequestFiles( typeof request.response.body === "string" && request.response.body.startsWith("file:") ) { - request.response.body = await readFile( + const buffer = await readFile( join(scenarioDirPath, request.response.body.substring(5)), ); + request.response.body = buffer.buffer; } requests.push(request); diff --git a/extensions/ql-vscode/src/common/query-language.ts b/extensions/ql-vscode/src/common/query-language.ts index b28ebedad00..03feb4b1353 100644 --- a/extensions/ql-vscode/src/common/query-language.ts +++ b/extensions/ql-vscode/src/common/query-language.ts @@ -1,4 +1,5 @@ export enum QueryLanguage { + Actions = "actions", CSharp = "csharp", Cpp = "cpp", Go = "go", @@ -11,7 +12,9 @@ export enum QueryLanguage { } export function getLanguageDisplayName(language: string): string { - switch (language) { + switch (language as QueryLanguage) { + case QueryLanguage.Actions: + return "Actions"; case QueryLanguage.CSharp: return "C#"; case QueryLanguage.Cpp: @@ -36,6 +39,7 @@ export function getLanguageDisplayName(language: string): string { } export const PACKS_BY_QUERY_LANGUAGE = { + [QueryLanguage.Actions]: ["codeql/actions-queries"], [QueryLanguage.Cpp]: ["codeql/cpp-queries"], [QueryLanguage.CSharp]: [ "codeql/csharp-queries", @@ -50,7 +54,7 @@ export const PACKS_BY_QUERY_LANGUAGE = { }; export const dbSchemeToLanguage: Record = { - "semmlecode.javascript.dbscheme": QueryLanguage.Javascript, + "semmlecode.javascript.dbscheme": QueryLanguage.Javascript, // This can also be QueryLanguage.Actions "semmlecode.cpp.dbscheme": QueryLanguage.Cpp, "semmlecode.dbscheme": QueryLanguage.Java, "semmlecode.python.dbscheme": QueryLanguage.Python, @@ -61,6 +65,18 @@ export const dbSchemeToLanguage: Record = { "swift.dbscheme": QueryLanguage.Swift, }; +export const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce( + (acc, [k, v]) => { + acc[v] = k; + return acc; + }, + {} as { [k: string]: string }, +); + +// Actions dbscheme is the same as Javascript dbscheme +languageToDbScheme[QueryLanguage.Actions] = + languageToDbScheme[QueryLanguage.Javascript]; + export function isQueryLanguage(language: string): language is QueryLanguage { return Object.values(QueryLanguage).includes(language as QueryLanguage); } diff --git a/extensions/ql-vscode/src/common/short-paths.ts b/extensions/ql-vscode/src/common/short-paths.ts index 838dac031e1..0025b96da05 100644 --- a/extensions/ql-vscode/src/common/short-paths.ts +++ b/extensions/ql-vscode/src/common/short-paths.ts @@ -1,7 +1,9 @@ -import { platform } from "os"; +import { arch, platform } from "os"; import { basename, dirname, join, normalize, resolve } from "path"; import { lstat, readdir } from "fs/promises"; import type { BaseLogger } from "./logging"; +import type { KoffiFunction } from "koffi"; +import { getErrorMessage } from "./helpers-pure"; /** * Expands a path that potentially contains 8.3 short names (e.g. "C:\PROGRA~1" instead of "C:\Program Files"). @@ -32,7 +34,23 @@ export async function expandShortPaths( return absoluteShortPath; } - return await expandShortPathRecursive(absoluteShortPath, logger); + const longPath = await expandShortPathRecursive(absoluteShortPath, logger); + if (longPath.indexOf("~") < 0) { + return longPath; + } + + void logger.log( + "Short path was not resolved to long path, using native method", + ); + + try { + return await expandShortPathNative(absoluteShortPath, logger); + } catch (e) { + void logger.log( + `Failed to expand short path using native method: ${getErrorMessage(e)}`, + ); + return longPath; + } } /** @@ -115,3 +133,46 @@ async function expandShortPathRecursive( const longBase = await expandShortPathComponent(dir, shortBase, logger); return join(dir, longBase); } + +let GetLongPathNameW: KoffiFunction | undefined; + +async function expandShortPathNative(shortPath: string, logger: BaseLogger) { + if (platform() !== "win32") { + throw new Error("expandShortPathNative is only supported on Windows"); + } + + if (arch() !== "x64") { + throw new Error( + "expandShortPathNative is only supported on x64 architecture", + ); + } + + if (GetLongPathNameW === undefined) { + // We are using koffi/indirect here to avoid including the native addon for all + // platforms in the bundle since this is only used on Windows. Instead, the + // native addon is included in the Gulpfile. + const koffi = await import("koffi/indirect"); + + const lib = koffi.load("kernel32.dll"); + GetLongPathNameW = lib.func("__stdcall", "GetLongPathNameW", "uint32", [ + "str16", + "str16", + "uint32", + ]); + } + + const MAX_PATH = 32767; + const buffer = Buffer.alloc(MAX_PATH * 2, 0); + + const result = GetLongPathNameW(shortPath, buffer, MAX_PATH); + + if (result === 0) { + throw new Error("Failed to get long path name"); + } + + const longPath = buffer.toString("utf16le", 0, (result - 1) * 2); + + void logger.log(`Expanded short path ${shortPath} to ${longPath}`); + + return longPath; +} diff --git a/extensions/ql-vscode/src/common/unzip.ts b/extensions/ql-vscode/src/common/unzip.ts index 2c8c361f562..9a35eedad38 100644 --- a/extensions/ql-vscode/src/common/unzip.ts +++ b/extensions/ql-vscode/src/common/unzip.ts @@ -5,6 +5,7 @@ import { Transform } from "stream"; import { dirname, join } from "path"; import type { WriteStream } from "fs"; import { createWriteStream, ensureDir } from "fs-extra"; +import { asError } from "./helpers-pure"; // We can't use promisify because it picks up the wrong overload. export function openZip( @@ -47,7 +48,7 @@ export function readZipEntries(zipFile: ZipFile): Promise { }); zipFile.on("error", (err) => { - reject(err); + reject(asError(err)); }); }); } diff --git a/extensions/ql-vscode/src/common/vscode/selection-commands.ts b/extensions/ql-vscode/src/common/vscode/selection-commands.ts index 93db99584a1..bfe107ff0a3 100644 --- a/extensions/ql-vscode/src/common/vscode/selection-commands.ts +++ b/extensions/ql-vscode/src/common/vscode/selection-commands.ts @@ -18,7 +18,7 @@ type SelectionCommand = CreateSupertypeOf< TreeViewContextMultiSelectionCommandFunction & TreeViewContextSingleSelectionCommandFunction & ExplorerSelectionCommandFunction, - (singleItem: T, multiSelect?: T[] | undefined) => Promise + (singleItem: T, multiSelect?: T[]) => Promise >; export function createSingleSelectionCommand( diff --git a/extensions/ql-vscode/src/compare-performance/compare-performance-view.ts b/extensions/ql-vscode/src/compare-performance/compare-performance-view.ts index c1633801ed6..a59367ea135 100644 --- a/extensions/ql-vscode/src/compare-performance/compare-performance-view.ts +++ b/extensions/ql-vscode/src/compare-performance/compare-performance-view.ts @@ -16,8 +16,9 @@ import { withProgress } from "../common/vscode/progress"; import { telemetryListener } from "../common/vscode/telemetry"; import type { HistoryItemLabelProvider } from "../query-history/history-item-label-provider"; import { PerformanceOverviewScanner } from "../log-insights/performance-comparison"; -import { scanLog } from "../log-insights/log-scanner"; import type { ResultsView } from "../local-queries"; +import { readJsonlFile } from "../common/jsonl-reader"; +import type { SummaryEvent } from "../log-insights/log-summary"; export class ComparePerformanceView extends AbstractWebview< ToComparePerformanceViewMessage, @@ -46,8 +47,20 @@ export class ComparePerformanceView extends AbstractWebview< function scanLogWithProgress(log: string, logDescription: string) { const bytes = statSync(log).size; return withProgress( - async (progress) => - scanLog(log, new PerformanceOverviewScanner(), progress), + async (progress) => { + progress?.({ + // all scans have step 1 - the backing progress tracker allows increments instead of + // steps - but for now we are happy with a tiny UI that says what is happening + message: `Scanning ...`, + step: 1, + maxStep: 2, + }); + const scanner = new PerformanceOverviewScanner(); + await readJsonlFile(log, async (obj) => { + scanner.onEvent(obj); + }); + return scanner; + }, { title: `Scanning evaluator log ${logDescription} (${(bytes / 1024 / 1024).toFixed(1)} MB)`, diff --git a/extensions/ql-vscode/src/compare/compare-view.ts b/extensions/ql-vscode/src/compare/compare-view.ts index c4c25df546a..93538f52e6b 100644 --- a/extensions/ql-vscode/src/compare/compare-view.ts +++ b/extensions/ql-vscode/src/compare/compare-view.ts @@ -70,22 +70,20 @@ export class CompareView extends AbstractWebview< selectedResultSetName?: string, ) { const [fromSchemas, toSchemas] = await Promise.all([ - this.cliServer.bqrsInfo( - from.completedQuery.query.resultsPaths.resultsPath, - ), - this.cliServer.bqrsInfo(to.completedQuery.query.resultsPaths.resultsPath), + this.cliServer.bqrsInfo(from.completedQuery.query.resultsPath), + this.cliServer.bqrsInfo(to.completedQuery.query.resultsPath), ]); const [fromSchemaNames, toSchemaNames] = await Promise.all([ getResultSetNames( fromSchemas, from.completedQuery.query.metadata, - from.completedQuery.query.resultsPaths.interpretedResultsPath, + from.completedQuery.query.interpretedResultsPath, ), getResultSetNames( toSchemas, to.completedQuery.query.metadata, - to.completedQuery.query.resultsPaths.interpretedResultsPath, + to.completedQuery.query.interpretedResultsPath, ), ]); @@ -101,15 +99,14 @@ export class CompareView extends AbstractWebview< schemaNames: fromSchemaNames, metadata: from.completedQuery.query.metadata, interpretedResultsPath: - from.completedQuery.query.resultsPaths.interpretedResultsPath, + from.completedQuery.query.interpretedResultsPath, }, to, toInfo: { schemas: toSchemas, schemaNames: toSchemaNames, metadata: to.completedQuery.query.metadata, - interpretedResultsPath: - to.completedQuery.query.resultsPaths.interpretedResultsPath, + interpretedResultsPath: to.completedQuery.query.interpretedResultsPath, }, commonResultSetNames, }; @@ -392,12 +389,12 @@ export class CompareView extends AbstractWebview< this.getResultSet( fromInfo.schemas, fromResultSetName, - from.completedQuery.query.resultsPaths.resultsPath, + from.completedQuery.query.resultsPath, ), this.getResultSet( toInfo.schemas, toResultSetName, - to.completedQuery.query.resultsPaths.resultsPath, + to.completedQuery.query.resultsPath, ), ]); diff --git a/extensions/ql-vscode/src/compare/interpreted-results.ts b/extensions/ql-vscode/src/compare/interpreted-results.ts index d5ca255ca4d..f98913a15e8 100644 --- a/extensions/ql-vscode/src/compare/interpreted-results.ts +++ b/extensions/ql-vscode/src/compare/interpreted-results.ts @@ -36,11 +36,9 @@ export async function compareInterpretedResults( const [fromResultSet, toResultSet, sourceLocationPrefix] = await Promise.all([ getInterpretedResults( - fromQuery.completedQuery.query.resultsPaths.interpretedResultsPath, - ), - getInterpretedResults( - toQuery.completedQuery.query.resultsPaths.interpretedResultsPath, + fromQuery.completedQuery.query.interpretedResultsPath, ), + getInterpretedResults(toQuery.completedQuery.query.interpretedResultsPath), database.getSourceLocationPrefix(cliServer), ]); diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 989e4931bcf..7e24fe26c5c 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -406,7 +406,7 @@ export class QueryServerConfigListener ): Promise { const codeQlPath = await distributionManager.getCodeQlPathWithoutVersionCheck(); - const config = new QueryServerConfigListener(codeQlPath!); + const config = new QueryServerConfigListener(codeQlPath); if (distributionManager.onDidChangeDistribution) { config.push( distributionManager.onDidChangeDistribution(async () => { @@ -954,3 +954,17 @@ export class GitHubDatabaseConfigListener await GITHUB_DATABASE_UPDATE.updateValue(value, target); } } + +const AUTOFIX_SETTING = new Setting("autofix", ROOT_SETTING); + +export const AUTOFIX_PATH = new Setting("path", AUTOFIX_SETTING); + +export function getAutofixPath(): string | undefined { + return AUTOFIX_PATH.getValue() || undefined; +} + +export const AUTOFIX_MODEL = new Setting("model", AUTOFIX_SETTING); + +export function getAutofixModel(): string | undefined { + return AUTOFIX_MODEL.getValue() || undefined; +} diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index d474f2d63e0..6f7f1356648 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -612,7 +612,7 @@ export class DatabaseFetcher { private async checkForFailingResponse( response: Response, errorMessage: string, - ): Promise { + ): Promise { if (response.ok) { return response; } diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index cfc53be40b6..d1ed2147fe7 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -284,6 +284,7 @@ export class DatabaseUI extends DisposableObject { this.handleUpgradeCurrentDatabase.bind(this), "codeQL.clearCache": this.handleClearCache.bind(this), "codeQL.trimCache": this.handleTrimCache.bind(this), + "codeQL.trimOverlayBaseCache": this.handleTrimOverlayBaseCache.bind(this), "codeQLDatabases.chooseDatabaseFolder": this.handleChooseDatabaseFolder.bind(this), "codeQLDatabases.chooseDatabaseArchive": @@ -505,7 +506,7 @@ export class DatabaseUI extends DisposableObject { ): Promise { try { await this.chooseAndSetDatabase(false, progress); - } catch (e: unknown) { + } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, this.app.telemetry, @@ -688,6 +689,25 @@ export class DatabaseUI extends DisposableObject { ); } + private async handleTrimOverlayBaseCache(): Promise { + return withProgress( + async () => { + if ( + this.queryServer !== undefined && + this.databaseManager.currentDatabaseItem !== undefined + ) { + await this.queryServer.trimCacheWithModeInDatabase( + this.databaseManager.currentDatabaseItem, + "overlay", + ); + } + }, + { + title: "Removing all overlay-dependent data from cache", + }, + ); + } + private async handleGetCurrentDatabase(): Promise { const dbItem = await this.getDatabaseItemInternal(undefined); return dbItem?.databaseUri.fsPath; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index f820625a5d8..643c601cd3e 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -411,7 +411,7 @@ export class DatabaseManager extends DisposableObject { qlpackStoragePath, ); await qlPackGenerator.generate(); - } catch (e: unknown) { + } catch (e) { void this.logger.log( `Could not create skeleton QL pack: ${getErrorMessage(e)}`, ); diff --git a/extensions/ql-vscode/src/databases/ui/db-panel.ts b/extensions/ql-vscode/src/databases/ui/db-panel.ts index 0605721d3ea..dbc61469d5a 100644 --- a/extensions/ql-vscode/src/databases/ui/db-panel.ts +++ b/extensions/ql-vscode/src/databases/ui/db-panel.ts @@ -432,7 +432,7 @@ export class DbPanel extends DisposableObject { try { // This will also validate that the controller repository is valid await getControllerRepo(this.app.credentials); - } catch (e: unknown) { + } catch (e) { if (e instanceof UserCancellationException) { return; } diff --git a/extensions/ql-vscode/src/debugger/debug-protocol.ts b/extensions/ql-vscode/src/debugger/debug-protocol.ts index 44e4fcf3b39..c24d72e412b 100644 --- a/extensions/ql-vscode/src/debugger/debug-protocol.ts +++ b/extensions/ql-vscode/src/debugger/debug-protocol.ts @@ -39,6 +39,7 @@ export interface EvaluationCompletedEvent extends Event { resultType: QueryResultType; message: string | undefined; evaluationTime: number; + outputBaseName: string; }; } diff --git a/extensions/ql-vscode/src/debugger/debug-session.ts b/extensions/ql-vscode/src/debugger/debug-session.ts index 1a51df30a30..64100a7831f 100644 --- a/extensions/ql-vscode/src/debugger/debug-session.ts +++ b/extensions/ql-vscode/src/debugger/debug-session.ts @@ -16,7 +16,7 @@ import type { BaseLogger, LogOptions } from "../common/logging"; import { queryServerLogger } from "../common/logging/vscode"; import { QueryResultType } from "../query-server/messages"; import type { - CoreQueryResults, + CoreQueryResult, CoreQueryRun, QueryRunner, } from "../query-server"; @@ -25,6 +25,7 @@ import type * as CodeQLProtocol from "./debug-protocol"; import type { QuickEvalContext } from "../run-queries-shared"; import { getErrorMessage } from "../common/helpers-pure"; import { DisposableObject } from "../common/disposable-object"; +import { basename } from "path"; // More complete implementations of `Event` for certain events, because the classes from // `@vscode/debugadapter` make it more difficult to provide some of the message values. @@ -107,9 +108,9 @@ class EvaluationCompletedEvent public readonly event = "codeql-evaluation-completed"; public readonly body: CodeQLProtocol.EvaluationCompletedEvent["body"]; - constructor(results: CoreQueryResults) { + constructor(result: CoreQueryResult) { super("codeql-evaluation-completed"); - this.body = results; + this.body = result; } } @@ -143,6 +144,7 @@ const QUERY_THREAD_NAME = "Evaluation thread"; class RunningQuery extends DisposableObject { private readonly tokenSource = this.push(new CancellationTokenSource()); public readonly queryRun: CoreQueryRun; + private readonly queryPath: string; public constructor( queryRunner: QueryRunner, @@ -154,21 +156,25 @@ class RunningQuery extends DisposableObject { ) { super(); + this.queryPath = config.query; // Create the query run, which will give us some information about the query even before the // evaluation has completed. this.queryRun = queryRunner.createQueryRun( config.database, - { - queryPath: config.query, - quickEvalPosition: quickEvalContext?.quickEvalPosition, - quickEvalCountOnly: quickEvalContext?.quickEvalCount, - }, + [ + { + queryPath: this.queryPath, + outputBaseName: "results", + quickEvalPosition: quickEvalContext?.quickEvalPosition, + quickEvalCountOnly: quickEvalContext?.quickEvalCount, + }, + ], true, config.additionalPacks, config.extensionPacks, config.additionalRunQueryArgs, queryStorageDir, - undefined, + basename(config.query), undefined, ); } @@ -208,7 +214,7 @@ class RunningQuery extends DisposableObject { progressStart.body.cancellable = true; this.sendEvent(progressStart); try { - return await this.queryRun.evaluate( + const completedQuery = await this.queryRun.evaluate( (p) => { const progressUpdate = new ProgressUpdateEvent( this.queryRun.id, @@ -220,6 +226,14 @@ class RunningQuery extends DisposableObject { this.tokenSource.token, this.logger, ); + return ( + completedQuery.results.get(this.queryPath) ?? { + resultType: QueryResultType.OTHER_ERROR, + message: "Missing query results", + evaluationTime: 0, + outputBaseName: "unknown", + } + ); } finally { this.sendEvent(new ProgressEndEvent(this.queryRun.id)); } @@ -229,6 +243,7 @@ class RunningQuery extends DisposableObject { resultType: QueryResultType.OTHER_ERROR, message, evaluationTime: 0, + outputBaseName: "unknown", }; } } diff --git a/extensions/ql-vscode/src/debugger/debugger-ui.ts b/extensions/ql-vscode/src/debugger/debugger-ui.ts index 6eb9a2d9fc7..8d401897055 100644 --- a/extensions/ql-vscode/src/debugger/debugger-ui.ts +++ b/extensions/ql-vscode/src/debugger/debugger-ui.ts @@ -8,7 +8,7 @@ import { debug, Uri, CancellationTokenSource } from "vscode"; import type { DebuggerCommands } from "../common/commands"; import type { DatabaseManager } from "../databases/local-databases"; import { DisposableObject } from "../common/disposable-object"; -import type { CoreQueryResults } from "../query-server"; +import type { CoreQueryResult } from "../query-server"; import { getQuickEvalContext, saveBeforeStart, @@ -134,8 +134,15 @@ class QLDebugAdapterTracker body: EvaluationCompletedEvent["body"], ): Promise { if (this.localQueryRun !== undefined) { - const results: CoreQueryResults = body; - await this.localQueryRun.complete(results, (_) => {}); + const results: CoreQueryResult = body; + await this.localQueryRun.complete( + { + results: new Map([ + [this.configuration.query, results], + ]), + }, + (_) => {}, + ); this.localQueryRun = undefined; } } diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index bf9e9be2b3d..b6766f3baff 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -28,7 +28,6 @@ import { CliConfigListener, DistributionConfigListener, GitHubDatabaseConfigListener, - joinOrderWarningThreshold, QueryHistoryConfigListener, QueryServerConfigListener, VariantAnalysisConfigListener, @@ -102,7 +101,6 @@ import { getPackagingCommands } from "./packaging"; import { HistoryItemLabelProvider } from "./query-history/history-item-label-provider"; import { EvalLogViewer } from "./query-evaluation-logging"; import { SummaryLanguageSupport } from "./log-insights/summary-language-support"; -import { JoinOrderScannerProvider } from "./log-insights/join-order"; import { LogScannerService } from "./log-insights/log-scanner-service"; import { VariantAnalysisView } from "./variant-analysis/variant-analysis-view"; import { VariantAnalysisViewSerializer } from "./variant-analysis/variant-analysis-view-serializer"; @@ -465,6 +463,25 @@ export async function activate( ); unsupportedWarningShown = true; }); + + // Expose the CodeQL CLI features to the extension context under `codeQL.cliFeatures.*`. + let cliFeatures: { [feature: string]: boolean | undefined } = {}; + codeQlExtension.cliServer.addVersionChangedListener(async (ver) => { + for (const feat of Object.keys(cliFeatures)) { + cliFeatures[feat] = false; + } + cliFeatures = { + ...cliFeatures, + ...(ver?.features ?? {}), + }; + for (const feat of Object.keys(cliFeatures)) { + await app.commands.execute( + "setContext", + `codeQL.cliFeatures.${feat}`, + cliFeatures[feat] ?? false, + ); + } + }); } return codeQlExtension; @@ -669,7 +686,7 @@ async function installOrUpdateThenTryActivate( try { await prepareCodeTour(app.commands); - } catch (e: unknown) { + } catch (e) { void extLogger.log( `Could not open tutorial workspace automatically: ${getErrorMessage(e)}`, ); @@ -941,11 +958,6 @@ async function activateWithInstalledDistribution( void extLogger.log("Initializing evaluation log scanners."); const logScannerService = new LogScannerService(qhm); ctx.subscriptions.push(logScannerService); - ctx.subscriptions.push( - logScannerService.scanners.registerLogScannerProvider( - new JoinOrderScannerProvider(() => joinOrderWarningThreshold()), - ), - ); void extLogger.log("Initializing compare view."); const compareView = new CompareView( diff --git a/extensions/ql-vscode/src/koffi.d.ts b/extensions/ql-vscode/src/koffi.d.ts new file mode 100644 index 00000000000..48cabce7265 --- /dev/null +++ b/extensions/ql-vscode/src/koffi.d.ts @@ -0,0 +1,4 @@ +// koffi/indirect is untyped in the upstream package, but it exports the same functions as koffi. +declare module "koffi/indirect" { + export * from "koffi"; +} diff --git a/extensions/ql-vscode/src/language-selection-panel/language-selection-data-provider.ts b/extensions/ql-vscode/src/language-selection-panel/language-selection-data-provider.ts index a182e29595d..fe0fb4c072c 100644 --- a/extensions/ql-vscode/src/language-selection-panel/language-selection-data-provider.ts +++ b/extensions/ql-vscode/src/language-selection-panel/language-selection-data-provider.ts @@ -9,6 +9,7 @@ import { const ALL_LANGUAGE_SELECTION_OPTIONS = [ undefined, // All languages + QueryLanguage.Actions, QueryLanguage.Cpp, QueryLanguage.CSharp, QueryLanguage.Go, diff --git a/extensions/ql-vscode/src/language-support/ast-viewer/ast-builder.ts b/extensions/ql-vscode/src/language-support/ast-viewer/ast-builder.ts index fd2203c0615..a8fd32a3276 100644 --- a/extensions/ql-vscode/src/language-support/ast-viewer/ast-builder.ts +++ b/extensions/ql-vscode/src/language-support/ast-viewer/ast-builder.ts @@ -7,7 +7,6 @@ import type { import type { DatabaseItem } from "../../databases/local-databases"; import type { ChildAstItem, AstItem } from "./ast-viewer"; import type { Uri } from "vscode"; -import type { QueryOutputDir } from "../../local-queries/query-output-dir"; import { fileRangeFromURI } from "../contextual/file-range-from-uri"; import { mapUrlValue } from "../../common/bqrs-raw-results-mapper"; @@ -17,15 +16,12 @@ import { mapUrlValue } from "../../common/bqrs-raw-results-mapper"; */ export class AstBuilder { private roots: AstItem[] | undefined; - private bqrsPath: string; constructor( - outputDir: QueryOutputDir, + private readonly bqrsPath: string, private cli: CodeQLCliServer, public db: DatabaseItem, public fileName: Uri, - ) { - this.bqrsPath = outputDir.bqrsPath; - } + ) {} async getRoots(): Promise { if (!this.roots) { diff --git a/extensions/ql-vscode/src/language-support/contextual/location-finder.ts b/extensions/ql-vscode/src/language-support/contextual/location-finder.ts index 01b8a5bbe76..0d3c25de93d 100644 --- a/extensions/ql-vscode/src/language-support/contextual/location-finder.ts +++ b/extensions/ql-vscode/src/language-support/contextual/location-finder.ts @@ -21,7 +21,6 @@ import { } from "./query-resolver"; import type { CancellationToken, LocationLink } from "vscode"; import { Uri } from "vscode"; -import type { QueryOutputDir } from "../../local-queries/query-output-dir"; import type { QueryRunner } from "../../query-server"; import { QueryResultType } from "../../query-server/messages"; import { fileRangeFromURI } from "./file-range-from-uri"; @@ -84,9 +83,15 @@ export async function getLocationsForUriString( token, templates, ); - if (results.resultType === QueryResultType.SUCCESS) { + const queryResult = results.results.get(query); + if (queryResult?.resultType === QueryResultType.SUCCESS) { links.push( - ...(await getLinksFromResults(results.outputDir, cli, db, filter)), + ...(await getLinksFromResults( + results.outputDir.getBqrsPath(queryResult.outputBaseName), + cli, + db, + filter, + )), ); } } @@ -94,13 +99,12 @@ export async function getLocationsForUriString( } async function getLinksFromResults( - outputDir: QueryOutputDir, + bqrsPath: string, cli: CodeQLCliServer, db: DatabaseItem, filter: (srcFile: string, destFile: string) => boolean, ): Promise { const localLinks: FullLocationLink[] = []; - const bqrsPath = outputDir.bqrsPath; const info = await cli.bqrsInfo(bqrsPath); const selectInfo = info["result-sets"].find( (schema) => schema.name === SELECT_QUERY_NAME, diff --git a/extensions/ql-vscode/src/language-support/contextual/query-resolver.ts b/extensions/ql-vscode/src/language-support/contextual/query-resolver.ts index 4624fa6f383..0fe2a08d1fc 100644 --- a/extensions/ql-vscode/src/language-support/contextual/query-resolver.ts +++ b/extensions/ql-vscode/src/language-support/contextual/query-resolver.ts @@ -14,6 +14,7 @@ import type { CancellationToken } from "vscode"; import type { ProgressCallback } from "../../common/vscode/progress"; import type { CoreCompletedQuery, QueryRunner } from "../../query-server"; import { createLockFileForStandardQuery } from "../../local-queries/standard-queries"; +import { basename } from "path"; /** * This wil try to determine the qlpacks for a given database. If it can't find a matching @@ -80,13 +81,19 @@ export async function runContextualQuery( const { cleanup } = await createLockFileForStandardQuery(cli, query); const queryRun = qs.createQueryRun( db.databaseUri.fsPath, - { queryPath: query, quickEvalPosition: undefined }, + [ + { + queryPath: query, + outputBaseName: "results", + quickEvalPosition: undefined, + }, + ], false, getOnDiskWorkspaceFolders(), undefined, {}, queryStorageDir, - undefined, + basename(query), templates, ); void extLogger.log( diff --git a/extensions/ql-vscode/src/language-support/contextual/template-provider.ts b/extensions/ql-vscode/src/language-support/contextual/template-provider.ts index 19927bd8903..8d9cf6327ec 100644 --- a/extensions/ql-vscode/src/language-support/contextual/template-provider.ts +++ b/extensions/ql-vscode/src/language-support/contextual/template-provider.ts @@ -133,7 +133,7 @@ export class TemplateQueryReferenceProvider implements ReferenceProvider { const locLinks: Location[] = []; for (const link of fileLinks) { - if (link.targetRange!.contains(position)) { + if (link.targetRange.contains(position)) { locLinks.push({ range: link.originSelectionRange!, uri: link.originUri, @@ -209,8 +209,14 @@ export class TemplatePrintAstProvider { ? await this.cache.get(fileUri.toString(), progress, token) : await this.getAst(fileUri.toString(), progress, token); + const queryResults = Array.from(completedQuery.results.values()); + if (queryResults.length !== 1) { + throw new Error( + `Expected exactly one query result, but found ${queryResults.length}.`, + ); + } return new AstBuilder( - completedQuery.outputDir, + completedQuery.outputDir.getBqrsPath(queryResults[0].outputBaseName), this.cli, this.dbm.findDatabaseItem(Uri.file(completedQuery.dbPath))!, fileUri, diff --git a/extensions/ql-vscode/src/language-support/language-client.ts b/extensions/ql-vscode/src/language-support/language-client.ts index 7b27a3fdaa5..07a3296b1af 100644 --- a/extensions/ql-vscode/src/language-support/language-client.ts +++ b/extensions/ql-vscode/src/language-support/language-client.ts @@ -79,7 +79,7 @@ async function spawnLanguageServer( languageServerLogger.log(data.toString(), { trailingNewline: false }), progressReporter, ); - return { writer: child.stdin!, reader: child.stdout! }; + return { writer: child.stdin, reader: child.stdout }; }, ); } diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index 2961586650b..4444ade8293 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -19,7 +19,11 @@ import { basename } from "path"; import { showBinaryChoiceDialog } from "../common/vscode/dialog"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { displayQuickQuery } from "./quick-query"; -import type { CoreCompletedQuery, QueryRunner } from "../query-server"; +import type { + CoreCompletedQuery, + CoreQueryTarget, + QueryRunner, +} from "../query-server"; import type { QueryHistoryManager } from "../query-history/query-history-manager"; import type { DatabaseQuickPickItem, @@ -37,6 +41,7 @@ import { createTimestampFile, getQuickEvalContext, saveBeforeStart, + validateQuerySuiteUri, validateQueryUri, } from "../run-queries-shared"; import type { CompletedLocalQueryInfo } from "../query-results"; @@ -51,6 +56,7 @@ import { LocalQueryRun } from "./local-query-run"; import { createMultiSelectionCommand } from "../common/vscode/selection-commands"; import { findLanguage } from "../codeql-cli/query-language"; import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; +import type { QueryLanguage } from "../common/query-language"; import { tryGetQueryLanguage } from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; import type { ExtensionApp } from "../common/vscode/extension-app"; @@ -107,6 +113,7 @@ export class LocalQueries extends DisposableObject { "codeQL.runQueries": createMultiSelectionCommand( this.runQueries.bind(this), ), + "codeQL.runQuerySuite": this.runQuerySuite.bind(this), "codeQL.quickEval": this.quickEval.bind(this), "codeQL.quickEvalCount": this.quickEvalCount.bind(this), "codeQL.quickEvalContextEditor": this.quickEval.bind(this), @@ -178,7 +185,7 @@ export class LocalQueries extends DisposableObject { private async runQueries(fileURIs: Uri[]): Promise { await withProgress( async (progress, token) => { - const maxQueryCount = MAX_QUERIES.getValue() as number; + const maxQueryCount = MAX_QUERIES.getValue(); const [files, dirFound] = await gatherQlFiles( fileURIs.map((uri) => uri.fsPath), ); @@ -239,6 +246,94 @@ export class LocalQueries extends DisposableObject { ); } + private async runQuerySuite(fileUri: Uri): Promise { + await withProgress( + async (progress, token) => { + const suitePath = validateQuerySuiteUri(fileUri); + const databaseItem = await this.databaseUI.getDatabaseItem(progress); + if (databaseItem === undefined) { + throw new Error("Can't run query suite without a selected database"); + } + const selectedQuery: SelectedQuery = { + queryPath: suitePath, + }; + const additionalPacks = getOnDiskWorkspaceFolders(); + const extensionPacks = + await this.getDefaultExtensionPacks(additionalPacks); + const queries = await this.cliServer.resolveQueriesInSuite( + suitePath, + additionalPacks, + ); + if ( + !(await showBinaryChoiceDialog( + `You are about to run ${basename(suitePath)}, which contains ${queries.length} queries. Do you want to continue?`, + )) + ) { + return; + } + const queryTargets: CoreQueryTarget[] = []; + queries.forEach((query, index) => { + queryTargets.push({ + queryPath: query, + outputBaseName: `${index.toString().padStart(3, "0")}-${basename(query)}`, + quickEvalPosition: undefined, + quickEvalCountOnly: false, + }); + }); + const coreQueryRun = this.queryRunner.createQueryRun( + databaseItem.databaseUri.fsPath, + queryTargets, + true, + additionalPacks, + extensionPacks, + {}, + this.queryStorageDir, + basename(suitePath), + undefined, + ); + // handle cancellation from the history view. + const source = new CancellationTokenSource(); + try { + token.onCancellationRequested(() => source.cancel()); + + const localQueryRun = await this.createLocalQueryRun( + selectedQuery, + databaseItem, + coreQueryRun.outputDir, + source, + ); + + try { + const results = await coreQueryRun.evaluate( + progress, + source.token, + localQueryRun.logger, + ); + + await localQueryRun.complete(results, progress); + + return results; + } catch (e) { + const err = asError(e); + await localQueryRun.fail(err); + + if (token.isCancellationRequested) { + throw new UserCancellationException(err.message, true); + } else { + throw e; + } + } + } finally { + source.dispose(); + } + }, + { + title: "Running query suite", + cancellable: true, + }, + ); + } + private async quickEval(uri: Uri): Promise { await withProgress( async (progress, token) => { @@ -452,17 +547,20 @@ export class LocalQueries extends DisposableObject { const coreQueryRun = this.queryRunner.createQueryRun( databaseItem.databaseUri.fsPath, - { - queryPath: selectedQuery.queryPath, - quickEvalPosition: selectedQuery.quickEval?.quickEvalPosition, - quickEvalCountOnly: selectedQuery.quickEval?.quickEvalCount, - }, + [ + { + queryPath: selectedQuery.queryPath, + outputBaseName: "results", + quickEvalPosition: selectedQuery.quickEval?.quickEvalPosition, + quickEvalCountOnly: selectedQuery.quickEval?.quickEvalCount, + }, + ], true, additionalPacks, extensionPacks, {}, this.queryStorageDir, - undefined, + basename(selectedQuery.queryPath), templates, ); @@ -524,7 +622,7 @@ export class LocalQueries extends DisposableObject { const queryLanguage = await findLanguage(this.cliServer, uri); if (queryLanguage) { filteredDBs = this.databaseManager.databaseItems.filter( - (db) => db.language === queryLanguage, + (db) => (db.language as QueryLanguage) === queryLanguage, ); if (filteredDBs.length === 0) { void showAndLogErrorMessage( diff --git a/extensions/ql-vscode/src/local-queries/local-query-run.ts b/extensions/ql-vscode/src/local-queries/local-query-run.ts index 5ea2463caef..cbe151a6601 100644 --- a/extensions/ql-vscode/src/local-queries/local-query-run.ts +++ b/extensions/ql-vscode/src/local-queries/local-query-run.ts @@ -4,7 +4,7 @@ import { showAndLogExceptionWithTelemetry, showAndLogWarningMessage, } from "../common/logging"; -import type { CoreQueryResults } from "../query-server"; +import type { CoreQueryResult, CoreQueryResults } from "../query-server"; import type { QueryHistoryManager } from "../query-history/query-history-manager"; import type { DatabaseItem } from "../databases/local-databases"; import type { @@ -29,7 +29,7 @@ import type { Disposable } from "../common/disposable-object"; import type { ProgressCallback } from "../common/vscode/progress"; import { progressUpdate } from "../common/vscode/progress"; -function formatResultMessage(result: CoreQueryResults): string { +function formatResultMessage(result: CoreQueryResult): string { switch (result.resultType) { case QueryResultType.CANCELLATION: return `cancelled after ${Math.round( @@ -86,7 +86,9 @@ export class LocalQueryRun { progress: ProgressCallback, ): Promise { const evalLogPaths = await this.summarizeEvalLog( - results.resultType, + Array.from(results.results.values()).every( + (result) => result.resultType === QueryResultType.SUCCESS, + ), this.outputDir, this.logger, progress, @@ -95,9 +97,12 @@ export class LocalQueryRun { this.queryInfo.setEvaluatorLogPaths(evalLogPaths); } progress(progressUpdate(1, 4, "Getting completed query info")); - const queryWithResults = await this.getCompletedQueryInfo(results); + const queriesWithResults = await this.getCompletedQueryInfo(results); progress(progressUpdate(2, 4, "Updating query history")); - this.queryHistoryManager.completeQuery(this.queryInfo, queryWithResults); + this.queryHistoryManager.completeQueries( + this.queryInfo, + queriesWithResults, + ); progress(progressUpdate(3, 4, "Showing results")); await this.localQueries.showResultsForCompletedQuery( this.queryInfo as CompletedLocalQueryInfo, @@ -116,7 +121,7 @@ export class LocalQueryRun { */ public async fail(err: Error): Promise { const evalLogPaths = await this.summarizeEvalLog( - QueryResultType.OTHER_ERROR, + false, this.outputDir, this.logger, (_) => {}, @@ -136,7 +141,7 @@ export class LocalQueryRun { * Generate summaries of the structured evaluator log. */ private async summarizeEvalLog( - resultType: QueryResultType, + runSuccessful: boolean, outputDir: QueryOutputDir, logger: BaseLogger, progress: ProgressCallback, @@ -152,7 +157,7 @@ export class LocalQueryRun { } } else { // Raw evaluator log was not found. Notify the user, unless we know why it wasn't found. - if (resultType === QueryResultType.SUCCESS) { + if (runSuccessful) { void showAndLogWarningMessage( extLogger, `Failed to write structured evaluator log to ${outputDir.evalLogPath}.`, @@ -168,41 +173,43 @@ export class LocalQueryRun { } /** - * Gets a `QueryWithResults` containing information about the evaluation of the query and its + * Gets a `QueryWithResults` containing information about the evaluation of the queries and their * result, in the form expected by the query history UI. */ private async getCompletedQueryInfo( results: CoreQueryResults, - ): Promise { - // Read the query metadata if possible, to use in the UI. - const metadata = await tryGetQueryMetadata( - this.cliServer, - this.queryInfo.initialInfo.queryPath, - ); - const query = new QueryEvaluationInfo( - this.outputDir.querySaveDir, - this.dbItem.databaseUri.fsPath, - await this.dbItem.hasMetadataFile(), - this.queryInfo.initialInfo.quickEvalPosition, - metadata, - ); + ): Promise { + const infos: QueryWithResults[] = []; + for (const [queryPath, result] of results.results) { + // Read the query metadata if possible, to use in the UI. + const metadata = await tryGetQueryMetadata(this.cliServer, queryPath); + const query = new QueryEvaluationInfo( + this.outputDir.querySaveDir, + result.outputBaseName, + this.dbItem.databaseUri.fsPath, + await this.dbItem.hasMetadataFile(), + undefined, + metadata, + ); - if (results.resultType !== QueryResultType.SUCCESS) { - const message = results.message - ? redactableError`Failed to run query: ${results.message}` - : redactableError`Failed to run query`; - void showAndLogExceptionWithTelemetry( - extLogger, - telemetryListener, + if (result.resultType !== QueryResultType.SUCCESS) { + const message = result.message + ? redactableError`Failed to run query: ${result.message}` + : redactableError`Failed to run query`; + void showAndLogExceptionWithTelemetry( + extLogger, + telemetryListener, + message, + ); + } + const message = formatResultMessage(result); + const successful = result.resultType === QueryResultType.SUCCESS; + infos.push({ + query, message, - ); + successful, + }); } - const message = formatResultMessage(results); - const successful = results.resultType === QueryResultType.SUCCESS; - return { - query, - message, - successful, - }; + return infos; } } diff --git a/extensions/ql-vscode/src/local-queries/query-contents.ts b/extensions/ql-vscode/src/local-queries/query-contents.ts index 762965f70eb..a7302026952 100644 --- a/extensions/ql-vscode/src/local-queries/query-contents.ts +++ b/extensions/ql-vscode/src/local-queries/query-contents.ts @@ -15,7 +15,7 @@ import { dbSchemeToLanguage } from "../common/query-language"; */ export function getInitialQueryContents(language: string, dbscheme: string) { if (!language) { - const dbschemeBase = basename(dbscheme) as keyof typeof dbSchemeToLanguage; + const dbschemeBase = basename(dbscheme); language = dbSchemeToLanguage[dbschemeBase]; } diff --git a/extensions/ql-vscode/src/local-queries/query-output-dir.ts b/extensions/ql-vscode/src/local-queries/query-output-dir.ts index a049849d54c..00be58078b7 100644 --- a/extensions/ql-vscode/src/local-queries/query-output-dir.ts +++ b/extensions/ql-vscode/src/local-queries/query-output-dir.ts @@ -30,10 +30,6 @@ function findQueryEvalLogEndSummaryFile(resultPath: string): string { export class QueryOutputDir { constructor(public readonly querySaveDir: string) {} - get dilPath() { - return join(this.querySaveDir, "results.dil"); - } - /** * Get the path that the compiled query is if it exists. Note that it only exists when using the legacy query server. */ @@ -41,10 +37,6 @@ export class QueryOutputDir { return join(this.querySaveDir, "compiledQuery.qlo"); } - get csvPath() { - return join(this.querySaveDir, "results.csv"); - } - get logPath() { return findQueryLogFile(this.querySaveDir); } @@ -69,7 +61,25 @@ export class QueryOutputDir { return findQueryEvalLogEndSummaryFile(this.querySaveDir); } - get bqrsPath() { - return join(this.querySaveDir, "results.bqrs"); + getBqrsPath(outputBaseName: string): string { + return join(this.querySaveDir, `${outputBaseName}.bqrs`); + } + + getInterpretedResultsPath( + metadataKind: string | undefined, + outputBaseName: string, + ): string { + return join( + this.querySaveDir, + `${outputBaseName}-${metadataKind === "graph" ? "graph" : `interpreted.sarif`}`, + ); + } + + getCsvPath(outputBaseName: string): string { + return join(this.querySaveDir, `${outputBaseName}.csv`); + } + + getDilPath(outputBaseName: string): string { + return join(this.querySaveDir, `${outputBaseName}.dil`); } } diff --git a/extensions/ql-vscode/src/local-queries/results-view.ts b/extensions/ql-vscode/src/local-queries/results-view.ts index 4e28e9f9c73..00eae1138aa 100644 --- a/extensions/ql-vscode/src/local-queries/results-view.ts +++ b/extensions/ql-vscode/src/local-queries/results-view.ts @@ -556,10 +556,14 @@ export class ResultsView extends AbstractWebview< await this.postMessage({ t: "setState", interpretation: interpretationPage, - origResultsPaths: fullQuery.completedQuery.query.resultsPaths, + origResultsPaths: { + resultsPath: fullQuery.completedQuery.query.resultsPath, + interpretedResultsPath: + fullQuery.completedQuery.query.interpretedResultsPath, + }, resultsPath: this.convertPathToWebviewUri( panel, - fullQuery.completedQuery.query.resultsPaths.resultsPath, + fullQuery.completedQuery.query.resultsPath, ), parsedResultSets, sortedResultsMap, @@ -704,10 +708,14 @@ export class ResultsView extends AbstractWebview< await this.postMessage({ t: "setState", interpretation: this._interpretation, - origResultsPaths: results.completedQuery.query.resultsPaths, + origResultsPaths: { + resultsPath: results.completedQuery.query.resultsPath, + interpretedResultsPath: + results.completedQuery.query.interpretedResultsPath, + }, resultsPath: this.convertPathToWebviewUri( panel, - results.completedQuery.query.resultsPaths.resultsPath, + results.completedQuery.query.resultsPath, ), parsedResultSets, sortedResultsMap, @@ -842,7 +850,10 @@ export class ResultsView extends AbstractWebview< }; await this._getInterpretedResults( query.metadata, - query.resultsPaths, + { + resultsPath: query.resultsPath, + interpretedResultsPath: query.interpretedResultsPath, + }, sourceInfo, sourceLocationPrefix, sortState, diff --git a/extensions/ql-vscode/src/local-queries/run-query.ts b/extensions/ql-vscode/src/local-queries/run-query.ts index 1f06c656b56..06ed7037280 100644 --- a/extensions/ql-vscode/src/local-queries/run-query.ts +++ b/extensions/ql-vscode/src/local-queries/run-query.ts @@ -33,17 +33,20 @@ export async function runQuery({ // Create a query run to execute const queryRun = queryRunner.createQueryRun( databaseItem.databaseUri.fsPath, - { - queryPath, - quickEvalPosition: undefined, - quickEvalCountOnly: false, - }, + [ + { + queryPath, + outputBaseName: "results", + quickEvalPosition: undefined, + quickEvalCountOnly: false, + }, + ], false, additionalPacks, extensionPacks, {}, queryStorageDir, - undefined, + basename(queryPath), undefined, ); @@ -54,13 +57,14 @@ export async function runQuery({ try { const completedQuery = await queryRun.evaluate(progress, token, teeLogger); + const result = completedQuery.results.get(queryPath); - if (completedQuery.resultType !== QueryResultType.SUCCESS) { + if (result?.resultType !== QueryResultType.SUCCESS) { void showAndLogExceptionWithTelemetry( extLogger, telemetryListener, redactableError`Failed to run ${basename(queryPath)} query: ${ - completedQuery.message ?? "No message" + result?.message ?? "No message" }`, ); return; diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 49d7bcaab11..f99eb8d2fda 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -38,6 +38,7 @@ import { getQlPackLanguage } from "../common/qlpack-language"; type QueryLanguagesToDatabaseMap = Record; export const QUERY_LANGUAGE_TO_DATABASE_REPO: QueryLanguagesToDatabaseMap = { + actions: "github/codeql", cpp: "google/brotli", csharp: "restsharp/RestSharp", go: "spf13/cobra", @@ -131,7 +132,7 @@ export class SkeletonQueryWizard { // open the query file try { await this.openExampleFile(); - } catch (e: unknown) { + } catch (e) { void this.app.logger.log( `Could not open example query file: ${getErrorMessage(e)}`, ); @@ -278,7 +279,7 @@ export class SkeletonQueryWizard { const qlPackGenerator = this.createQlPackGenerator(); await qlPackGenerator.generate(); - } catch (e: unknown) { + } catch (e) { void this.app.logger.log( `Could not create skeleton QL pack: ${getErrorMessage(e)}`, ); @@ -298,7 +299,7 @@ export class SkeletonQueryWizard { this.fileName = await this.determineNextFileName(); await qlPackGenerator.createExampleQlFile(this.fileName); - } catch (e: unknown) { + } catch (e) { void this.app.logger.log( `Could not create query example file: ${getErrorMessage(e)}`, ); @@ -341,7 +342,7 @@ export class SkeletonQueryWizard { void withProgress(async (progress) => { try { await this.downloadDatabase(progress); - } catch (e: unknown) { + } catch (e) { if (e instanceof UserCancellationException) { return; } diff --git a/extensions/ql-vscode/src/log-insights/join-order.ts b/extensions/ql-vscode/src/log-insights/join-order.ts index 788da0a98e6..4bf85c89402 100644 --- a/extensions/ql-vscode/src/log-insights/join-order.ts +++ b/extensions/ql-vscode/src/log-insights/join-order.ts @@ -1,8 +1,5 @@ -import type { - EvaluationLogProblemReporter, - EvaluationLogScanner, - EvaluationLogScannerProvider, -} from "./log-scanner"; +import { readJsonlFile } from "../common/jsonl-reader"; +import type { EvaluationLogProblemReporter } from "./log-scanner"; import type { InLayer, ComputeRecursive, @@ -19,23 +16,6 @@ function safeMax(it?: Iterable) { return Number.isFinite(m) ? m : 0; } -/** - * Compute a key for the maps that that is sent to report generation. - * Should only be used on events that are known to define queryCausingWork. - */ -function makeKey( - queryCausingWork: string | undefined, - predicate: string, - suffix = "", -): string { - if (queryCausingWork === undefined) { - throw new Error( - "queryCausingWork was not defined on an event we expected it to be defined for!", - ); - } - return `${queryCausingWork}:${predicate}${suffix ? ` ${suffix}` : ""}`; -} - function getDependentPredicates(operations: string[]): string[] { const id = String.raw`[0-9a-zA-Z:#_\./]+`; const idWithAngleBrackets = String.raw`[0-9a-zA-Z:#_<>\./]+`; @@ -128,14 +108,6 @@ function pointwiseSum( return result; } -function pushValue(m: Map, k: K, v: V) { - if (!m.has(k)) { - m.set(k, []); - } - m.get(k)!.push(v); - return m; -} - function computeJoinOrderBadness( maxTupleCount: number, maxDependentPredicateSize: number, @@ -154,41 +126,18 @@ interface Bucket { dependentPredicateSizes: Map; } -class JoinOrderScanner implements EvaluationLogScanner { +class PredicateSizeScanner { // Map a predicate hash to its result size - private readonly predicateSizes = new Map(); - private readonly layerEvents = new Map< - string, - Array - >(); - // Map a key of the form 'query-with-demand : predicate name' to its badness input. - private readonly maxTupleCountMap = new Map(); - private readonly resultSizeMap = new Map(); - private readonly maxDependentPredicateSizeMap = new Map(); - private readonly joinOrderMetricMap = new Map(); - - constructor( - private readonly problemReporter: EvaluationLogProblemReporter, - private readonly warningThreshold: number, - ) {} + readonly predicateSizes = new Map(); + readonly layerEvents = new Map>(); - public onEvent(event: SummaryEvent): void { + onEvent(event: SummaryEvent): void { if ( event.completionType !== undefined && event.completionType !== "SUCCESS" ) { return; // Skip any evaluation that wasn't successful } - - this.recordPredicateSizes(event); - this.computeBadnessMetric(event); - } - - public onDone(): void { - void this; - } - - private recordPredicateSizes(event: SummaryEvent): void { switch (event.evaluationStrategy) { case "EXTENSIONAL": case "COMPUTED_EXTENSIONAL": @@ -215,29 +164,20 @@ class JoinOrderScanner implements EvaluationLogScanner { } } } +} - private reportProblemIfNecessary( - event: SummaryEvent, - iteration: number, - metric: number, - ): void { - if (metric >= this.warningThreshold) { - this.problemReporter.reportProblem( - event.predicateName, - event.raHash, - iteration, - `Relation '${ - event.predicateName - }' has an inefficient join order. Its join order metric is ${metric.toFixed( - 2, - )}, which is larger than the threshold of ${this.warningThreshold.toFixed( - 2, - )}.`, - ); - } - } +class JoinOrderScanner { + constructor( + private readonly predicateSizes: Map, + private readonly layerEvents: Map< + string, + Array + >, + private readonly problemReporter: EvaluationLogProblemReporter, + private readonly warningThreshold: number, + ) {} - private computeBadnessMetric(event: SummaryEvent): void { + public onEvent(event: SummaryEvent): void { if ( event.completionType !== undefined && event.completionType !== "SUCCESS" @@ -252,7 +192,6 @@ class JoinOrderScanner implements EvaluationLogScanner { } // Compute the badness metric for a non-recursive predicate. The metric in this case is defined as: // badness = (max tuple count in the pipeline) / (largest predicate this pipeline depends on) - const key = makeKey(event.queryCausingWork, event.predicateName); const resultSize = event.resultSize; // There is only one entry in `pipelineRuns` if it's a non-recursive predicate. @@ -260,20 +199,26 @@ class JoinOrderScanner implements EvaluationLogScanner { this.badnessInputsForNonRecursiveDelta(event.pipelineRuns[0], event); if (maxDependentPredicateSize > 0) { - pushValue(this.maxTupleCountMap, key, maxTupleCount); - pushValue(this.resultSizeMap, key, resultSize); - pushValue( - this.maxDependentPredicateSizeMap, - key, - maxDependentPredicateSize, - ); const metric = computeJoinOrderBadness( maxTupleCount, maxDependentPredicateSize, - resultSize!, + resultSize, ); - this.joinOrderMetricMap.set(key, metric); - this.reportProblemIfNecessary(event, 0, metric); + if (metric >= this.warningThreshold) { + const message = `'${event.predicateName}@${event.raHash.substring( + 0, + 8, + )}' has an inefficient join order. Its join order metric is ${metric.toFixed( + 2, + )}, which is larger than the threshold of ${this.warningThreshold.toFixed( + 2, + )}.`; + this.problemReporter.reportProblemNonRecursive( + event.predicateName, + event.raHash, + message, + ); + } } break; } @@ -282,39 +227,40 @@ class JoinOrderScanner implements EvaluationLogScanner { // Compute the badness metric for a recursive predicate for each ordering. const sccMetricInput = this.badnessInputsForRecursiveDelta(event); // Loop through each predicate in the SCC - sccMetricInput.forEach((buckets, predicate) => { - // Loop through each ordering of the predicate - buckets.forEach((bucket, raReference) => { - // Format the key as demanding-query:name (ordering) - const key = makeKey( - event.queryCausingWork, - predicate, - `(${raReference})`, - ); - const maxTupleCount = Math.max(...bucket.tupleCounts); - const resultSize = bucket.resultSize; - const maxDependentPredicateSize = Math.max( - ...bucket.dependentPredicateSizes.values(), - ); - - if (maxDependentPredicateSize > 0) { - pushValue(this.maxTupleCountMap, key, maxTupleCount); - pushValue(this.resultSizeMap, key, resultSize); - pushValue( - this.maxDependentPredicateSizeMap, - key, - maxDependentPredicateSize, - ); - const metric = computeJoinOrderBadness( - maxTupleCount, - maxDependentPredicateSize, - resultSize, + sccMetricInput.forEach((hashToOrderToBucket, predicateName) => { + hashToOrderToBucket.forEach((orderToBucket, raHash) => { + // Loop through each ordering of the predicate. + orderToBucket.forEach((bucket, raReference) => { + const maxDependentPredicateSize = Math.max( + ...bucket.dependentPredicateSizes.values(), ); - const oldMetric = this.joinOrderMetricMap.get(key); - if (oldMetric === undefined || metric > oldMetric) { - this.joinOrderMetricMap.set(key, metric); + + if (maxDependentPredicateSize > 0) { + const maxTupleCount = Math.max(...bucket.tupleCounts); + const resultSize = bucket.resultSize; + const metric = computeJoinOrderBadness( + maxTupleCount, + maxDependentPredicateSize, + resultSize, + ); + if (metric >= this.warningThreshold) { + const message = `The ${raReference} pipeline for '${predicateName}@${raHash.substring( + 0, + 8, + )}' has an inefficient join order. Its join order metric is ${metric.toFixed( + 2, + )}, which is larger than the threshold of ${this.warningThreshold.toFixed( + 2, + )}.`; + this.problemReporter.reportProblemForRecursionSummary( + predicateName, + raHash, + raReference, + message, + ); + } } - } + }); }); }); break; @@ -457,20 +403,28 @@ class JoinOrderScanner implements EvaluationLogScanner { */ private badnessInputsForRecursiveDelta( event: ComputeRecursive, - ): Map> { - // nameToOrderToBucket : predicate name -> ordering (i.e., standard, order_500000, etc.) -> bucket - const nameToOrderToBucket = new Map>(); + ): Map>> { + // nameToHashToOrderToBucket : predicate name -> RA hash -> ordering (i.e., standard, order_500000, etc.) -> bucket + const nameToHashToOrderToBucket = new Map< + string, + Map> + >(); // Iterate through the SCC and compute the metric inputs this.iterateSCC(event, (inLayerEvent, run, iteration) => { const raReference = run.raReference; const predicateName = inLayerEvent.predicateName; - if (!nameToOrderToBucket.has(predicateName)) { - nameToOrderToBucket.set(predicateName, new Map()); + if (!nameToHashToOrderToBucket.has(predicateName)) { + nameToHashToOrderToBucket.set(predicateName, new Map()); } - const orderTobucket = nameToOrderToBucket.get(predicateName)!; - if (!orderTobucket.has(raReference)) { - orderTobucket.set(raReference, { + const hashToOrderToBucket = nameToHashToOrderToBucket.get(predicateName)!; + const raHash = inLayerEvent.raHash; + if (!hashToOrderToBucket.has(raHash)) { + hashToOrderToBucket.set(raHash, new Map()); + } + const orderToBucket = hashToOrderToBucket.get(raHash)!; + if (!orderToBucket.has(raReference)) { + orderToBucket.set(raReference, { tupleCounts: new Int32Array(0), resultSize: 0, dependentPredicateSizes: new Map(), @@ -484,7 +438,7 @@ class JoinOrderScanner implements EvaluationLogScanner { iteration, ); - const bucket = orderTobucket.get(raReference)!; + const bucket = orderToBucket.get(raReference)!; // Pointwise sum the tuple counts const newTupleCounts = pointwiseSum( bucket.tupleCounts, @@ -504,23 +458,36 @@ class JoinOrderScanner implements EvaluationLogScanner { ); } - orderTobucket.set(raReference, { + orderToBucket.set(raReference, { tupleCounts: newTupleCounts, resultSize, dependentPredicateSizes: newDependentPredicateSizes, }); }); - return nameToOrderToBucket; + return nameToHashToOrderToBucket; } } -export class JoinOrderScannerProvider implements EvaluationLogScannerProvider { - constructor(private readonly getThreshdold: () => number) {} +export async function scanAndReportJoinOrderProblems( + jsonSummaryLocation: string, + problemReporter: EvaluationLogProblemReporter, + warningThreshold: number, +) { + // Do two passes over the summary JSON. The first pass collects the sizes of predicates, along + // with collecting layer events for each recursive SCC. + const predicateSizeScanner = new PredicateSizeScanner(); + await readJsonlFile(jsonSummaryLocation, async (obj) => { + predicateSizeScanner.onEvent(obj); + }); - public createScanner( - problemReporter: EvaluationLogProblemReporter, - ): EvaluationLogScanner { - const threshold = this.getThreshdold(); - return new JoinOrderScanner(problemReporter, threshold); - } + // The second pass takes the information from the first pass, computes join order scores, and reports those that exceed the threshold. + const joinOrderScanner = new JoinOrderScanner( + predicateSizeScanner.predicateSizes, + predicateSizeScanner.layerEvents, + problemReporter, + warningThreshold, + ); + await readJsonlFile(jsonSummaryLocation, async (obj) => { + joinOrderScanner.onEvent(obj); + }); } diff --git a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts index 3d78bb30aa8..e8e79d60bc5 100644 --- a/extensions/ql-vscode/src/log-insights/log-scanner-service.ts +++ b/extensions/ql-vscode/src/log-insights/log-scanner-service.ts @@ -2,11 +2,12 @@ import { Diagnostic, DiagnosticSeverity, languages, Range, Uri } from "vscode"; import { DisposableObject } from "../common/disposable-object"; import type { QueryHistoryInfo } from "../query-history/query-history-info"; import type { EvaluationLogProblemReporter } from "./log-scanner"; -import { EvaluationLogScannerSet } from "./log-scanner"; import type { PipelineInfo, SummarySymbols } from "./summary-parser"; import { readFile } from "fs-extra"; import { extLogger } from "../common/logging/vscode"; import type { QueryHistoryManager } from "../query-history/query-history-manager"; +import { scanAndReportJoinOrderProblems } from "./join-order"; +import { joinOrderWarningThreshold } from "../config"; /** * Compute the key used to find a predicate in the summary symbols. @@ -28,17 +29,41 @@ class ProblemReporter implements EvaluationLogProblemReporter { constructor(private readonly symbols: SummarySymbols | undefined) {} - public reportProblem( + public reportProblemNonRecursive( predicateName: string, raHash: string, - iteration: number, message: string, ): void { const nameWithHash = predicateSymbolKey(predicateName, raHash); const predicateSymbol = this.symbols?.predicates[nameWithHash]; let predicateInfo: PipelineInfo | undefined = undefined; if (predicateSymbol !== undefined) { - predicateInfo = predicateSymbol.iterations[iteration]; + predicateInfo = predicateSymbol.iterations[0]; + } + if (predicateInfo !== undefined) { + const range = new Range( + predicateInfo.raStartLine, + 0, + predicateInfo.raEndLine + 1, + 0, + ); + this.diagnostics.push( + new Diagnostic(range, message, DiagnosticSeverity.Error), + ); + } + } + + public reportProblemForRecursionSummary( + predicateName: string, + raHash: string, + order: string, + message: string, + ): void { + const nameWithHash = predicateSymbolKey(predicateName, raHash); + const predicateSymbol = this.symbols?.predicates[nameWithHash]; + let predicateInfo: PipelineInfo | undefined = undefined; + if (predicateSymbol !== undefined) { + predicateInfo = predicateSymbol.recursionSummaries[order]; } if (predicateInfo !== undefined) { const range = new Range( @@ -59,7 +84,6 @@ class ProblemReporter implements EvaluationLogProblemReporter { } export class LogScannerService extends DisposableObject { - public readonly scanners = new EvaluationLogScannerSet(); private readonly diagnosticCollection = this.push( languages.createDiagnosticCollection("ql-eval-log"), ); @@ -127,9 +151,11 @@ export class LogScannerService extends DisposableObject { ); } const problemReporter = new ProblemReporter(symbols); - - await this.scanners.scanLog(jsonSummaryLocation, problemReporter); - + await scanAndReportJoinOrderProblems( + jsonSummaryLocation, + problemReporter, + joinOrderWarningThreshold(), + ); return problemReporter.diagnostics; } } diff --git a/extensions/ql-vscode/src/log-insights/log-scanner.ts b/extensions/ql-vscode/src/log-insights/log-scanner.ts index 0ad775e6ec1..22a9a5cdf78 100644 --- a/extensions/ql-vscode/src/log-insights/log-scanner.ts +++ b/extensions/ql-vscode/src/log-insights/log-scanner.ts @@ -1,139 +1,38 @@ -import type { Disposable } from "../common/disposable-object"; -import { readJsonlFile } from "../common/jsonl-reader"; -import type { ProgressCallback } from "../common/vscode/progress"; -import type { SummaryEvent } from "./log-summary"; - /** * Callback interface used to report diagnostics from a log scanner. */ export interface EvaluationLogProblemReporter { /** - * Report a potential problem detected in the evaluation log. + * Report a potential problem detected in the evaluation log for a non-recursive predicate. * * @param predicateName The mangled name of the predicate with the problem. * @param raHash The RA hash of the predicate with the problem. - * @param iteration The iteration number with the problem. For a non-recursive predicate, this - * must be zero. * @param message The problem message. */ - reportProblem( + reportProblemNonRecursive( predicateName: string, raHash: string, - iteration: number, message: string, ): void; /** - * Log a message about a problem in the implementation of the scanner. These will typically be - * displayed separate from any problems reported via `reportProblem()`. - */ - log(message: string): void; -} - -/** - * Interface implemented by a log scanner. Instances are created via - * `EvaluationLogScannerProvider.createScanner()`. - */ -export interface EvaluationLogScanner { - /** - * Called for each event in the log summary, in order. The implementation can report problems via - * the `EvaluationLogProblemReporter` interface that was supplied to `createScanner()`. - * @param event The log summary event. - */ - onEvent(event: SummaryEvent): void; - /** - * Called after all events in the log summary have been processed. The implementation can report - * problems via the `EvaluationLogProblemReporter` interface that was supplied to - * `createScanner()`. - */ - onDone(): void; -} - -/** - * A factory for log scanners. When a log is to be scanned, all registered - * `EvaluationLogScannerProviders` will be asked to create a new instance of `EvaluationLogScanner` - * to do the scanning. - */ -export interface EvaluationLogScannerProvider { - /** - * Create a new instance of `EvaluationLogScanner` to scan a single summary log. - * @param problemReporter Callback interface for reporting any problems discovered. - */ - createScanner( - problemReporter: EvaluationLogProblemReporter, - ): EvaluationLogScanner; -} - -export class EvaluationLogScannerSet { - private readonly scannerProviders = new Map< - number, - EvaluationLogScannerProvider - >(); - private nextScannerProviderId = 0; - - /** - * Register a provider that can create instances of `EvaluationLogScanner` to scan evaluation logs - * for problems. - * @param provider The provider. - * @returns A `Disposable` that, when disposed, will unregister the provider. + * Report a potential problem detected in the evaluation log for the summary of a recursive pipeline. + * + * @param predicateName The mangled name of the predicate with the problem. + * @param raHash The RA hash of the predicate with the problem. + * @param order The particular order (pipeline name) that had the problem. + * @param message The problem message. */ - public registerLogScannerProvider( - provider: EvaluationLogScannerProvider, - ): Disposable { - const id = this.nextScannerProviderId; - this.nextScannerProviderId++; - - this.scannerProviders.set(id, provider); - return { - dispose: () => { - this.scannerProviders.delete(id); - }, - }; - } + reportProblemForRecursionSummary( + predicateName: string, + raHash: string, + order: string, + message: string, + ): void; /** - * Scan the evaluator summary log for problems, using the scanners for all registered providers. - * @param jsonSummaryLocation The file path of the JSON summary log. - * @param problemReporter Callback interface for reporting any problems discovered. + * Log a message about a problem in the implementation of the scanner. These will typically be + * displayed separate from any problems reported via `reportProblem()`. */ - public async scanLog( - jsonSummaryLocation: string, - problemReporter: EvaluationLogProblemReporter, - ): Promise { - const scanners = [...this.scannerProviders.values()].map((p) => - p.createScanner(problemReporter), - ); - - await readJsonlFile(jsonSummaryLocation, async (obj) => { - scanners.forEach((scanner) => { - scanner.onEvent(obj); - }); - }); - - scanners.forEach((scanner) => scanner.onDone()); - } -} - -/** - * Scan the evaluator summary log using the given scanner. For convenience, returns the scanner. - * - * @param jsonSummaryLocation The file path of the JSON summary log. - * @param scanner The scanner to process events from the log - */ -export async function scanLog( - jsonSummaryLocation: string, - scanner: T, - progress?: ProgressCallback, -): Promise { - progress?.({ - // all scans have step 1 - the backing progress tracker allows increments instead of steps - but for now we are happy with a tiny UI that says what is happening - message: `Scanning ...`, - step: 1, - maxStep: 2, - }); - await readJsonlFile(jsonSummaryLocation, async (obj) => { - scanner.onEvent(obj); - }); - scanner.onDone(); - return scanner; + log(message: string): void; } diff --git a/extensions/ql-vscode/src/log-insights/log-summary.ts b/extensions/ql-vscode/src/log-insights/log-summary.ts index 5fa4bda58b8..6919210f98c 100644 --- a/extensions/ql-vscode/src/log-insights/log-summary.ts +++ b/extensions/ql-vscode/src/log-insights/log-summary.ts @@ -16,7 +16,8 @@ type EvaluationStrategy = | "EXTENSIONAL" | "SENTINEL_EMPTY" | "CACHACA" - | "CACHE_HIT"; + | "CACHE_HIT" + | "NAMED_LOCAL"; interface SummaryEventBase { evaluationStrategy: EvaluationStrategy; @@ -28,6 +29,8 @@ interface SummaryEventBase { interface ResultEventBase extends SummaryEventBase { resultSize: number; + dependencies?: { [key: string]: string }; + mainHash?: string; } export interface ComputeSimple extends ResultEventBase { @@ -60,6 +63,15 @@ export interface InLayer extends ResultEventBase { predicateIterationMillis: number[]; } +interface NamedLocal extends ResultEventBase { + evaluationStrategy: "NAMED_LOCAL"; + deltaSizes: number[]; + ra: Ra; + pipelineRuns: PipelineRun[]; + queryCausingWork?: string; + predicateIterationMillis: number[]; +} + interface ComputedExtensional extends ResultEventBase { evaluationStrategy: "COMPUTED_EXTENSIONAL"; queryCausingWork?: string; @@ -92,4 +104,5 @@ export type SummaryEvent = | Extensional | SentinelEmpty | Cachaca - | CacheHit; + | CacheHit + | NamedLocal; diff --git a/extensions/ql-vscode/src/log-insights/performance-comparison.ts b/extensions/ql-vscode/src/log-insights/performance-comparison.ts index 46634cf8bf3..7dfb516b9c3 100644 --- a/extensions/ql-vscode/src/log-insights/performance-comparison.ts +++ b/extensions/ql-vscode/src/log-insights/performance-comparison.ts @@ -1,10 +1,11 @@ -import type { EvaluationLogScanner } from "./log-scanner"; +import { createHash } from "crypto"; import type { SummaryEvent } from "./log-summary"; export interface PipelineSummary { steps: string[]; /** Total counts for each step in the RA array, across all iterations */ counts: number[]; + hash: string; } /** @@ -27,6 +28,9 @@ export interface PerformanceComparisonDataFromLog { */ names: string[]; + /** RA hash of the `i`th predicate event */ + raHashes: string[]; + /** Number of milliseconds spent evaluating the `i`th predicate from the `names` array. */ timeCosts: number[]; @@ -53,12 +57,15 @@ export interface PerformanceComparisonDataFromLog { * All the pipeline runs seen for the `i`th predicate from the `names` array. */ pipelineSummaryList: Array>; + + /** All dependencies of the `i`th predicate from the `names` array, encoded as a list of indices in `names`. */ + dependencyLists: number[][]; } -export class PerformanceOverviewScanner implements EvaluationLogScanner { - private readonly nameToIndex = new Map(); +export class PerformanceOverviewScanner { private readonly data: PerformanceComparisonDataFromLog = { names: [], + raHashes: [], timeCosts: [], tupleCosts: [], cacheHitIndices: [], @@ -66,28 +73,35 @@ export class PerformanceOverviewScanner implements EvaluationLogScanner { pipelineSummaryList: [], evaluationCounts: [], iterationCounts: [], + dependencyLists: [], }; + private readonly raToIndex = new Map(); + private readonly mainHashToRepr = new Map(); + private readonly nameToIndex = new Map(); - private getPredicateIndex(name: string): number { - const { nameToIndex } = this; - let index = nameToIndex.get(name); + private getPredicateIndex(name: string, ra: string): number { + let index = this.raToIndex.get(ra); if (index === undefined) { - index = nameToIndex.size; - nameToIndex.set(name, index); + index = this.raToIndex.size; + this.raToIndex.set(ra, index); const { names, + raHashes, timeCosts, tupleCosts, iterationCounts, evaluationCounts, pipelineSummaryList, + dependencyLists, } = this.data; names.push(name); + raHashes.push(ra); timeCosts.push(0); tupleCosts.push(0); iterationCounts.push(0); evaluationCounts.push(0); pipelineSummaryList.push({}); + dependencyLists.push([]); } return index; } @@ -97,46 +111,63 @@ export class PerformanceOverviewScanner implements EvaluationLogScanner { } onEvent(event: SummaryEvent): void { - if ( - event.completionType !== undefined && - event.completionType !== "SUCCESS" - ) { + const { completionType, evaluationStrategy, predicateName, raHash } = event; + if (completionType !== undefined && completionType !== "SUCCESS") { return; // Skip any evaluation that wasn't successful } - switch (event.evaluationStrategy) { - case "EXTENSIONAL": + switch (evaluationStrategy) { + case "EXTENSIONAL": { + break; + } case "COMPUTED_EXTENSIONAL": { + if (predicateName.startsWith("cached_")) { + // Add a dependency from a cached COMPUTED_EXTENSIONAL to the predicate with the actual contents. + // The raHash of the this event may appear in a CACHE_HIT event in the other event log. The dependency + // we're adding here is needed in order to associate the original predicate with such a cache hit. + const originalName = predicateName.substring("cached_".length); + const originalIndex = this.nameToIndex.get(originalName); + if (originalIndex != null) { + const index = this.getPredicateIndex(predicateName, raHash); + this.data.dependencyLists[index].push(originalIndex); + } + } break; } case "CACHE_HIT": case "CACHACA": { // Record a cache hit, but only if the predicate has not been seen before. // We're mainly interested in the reuse of caches from an earlier query run as they can distort comparisons. - if (!this.nameToIndex.has(event.predicateName)) { + if (!this.raToIndex.has(raHash)) { this.data.cacheHitIndices.push( - this.getPredicateIndex(event.predicateName), + this.getPredicateIndex(predicateName, raHash), ); } break; } case "SENTINEL_EMPTY": { - this.data.sentinelEmptyIndices.push( - this.getPredicateIndex(event.predicateName), - ); + const index = this.getPredicateIndex(predicateName, raHash); + this.data.sentinelEmptyIndices.push(index); + const sentinelIndex = this.raToIndex.get(event.sentinelRaHash); + if (sentinelIndex != null) { + this.data.dependencyLists[index].push(sentinelIndex); // needed for matching up cache hits + } break; } case "COMPUTE_RECURSIVE": case "COMPUTE_SIMPLE": + case "NAMED_LOCAL": case "IN_LAYER": { - const index = this.getPredicateIndex(event.predicateName); + const index = this.getPredicateIndex(predicateName, raHash); + this.nameToIndex.set(predicateName, index); let totalTime = 0; let totalTuples = 0; - if (event.evaluationStrategy !== "IN_LAYER") { + if (evaluationStrategy === "COMPUTE_SIMPLE") { totalTime += event.millis; } else { - // IN_LAYER events do no record of their total time. - // Make a best-effort estimate by adding up the positive iteration times (they can be negative). + // Make a best-effort estimate of the total time by adding up the positive iteration times (they can be negative). + // Note that for COMPUTE_RECURSIVE the "millis" field contain the total time of the SCC, not just that predicate, + // but we don't have a good way to show that in the UI, so we rely on the accumulated iteration times. for (const millis of event.predicateIterationMillis ?? []) { if (millis > 0) { totalTime += millis; @@ -149,13 +180,16 @@ export class PerformanceOverviewScanner implements EvaluationLogScanner { iterationCounts, evaluationCounts, pipelineSummaryList, + dependencyLists, } = this.data; const pipelineSummaries = pipelineSummaryList[index]; + const dependencyList = dependencyLists[index]; for (const { counts, raReference } of event.pipelineRuns ?? []) { // Get or create the pipeline summary for this RA const pipelineSummary = (pipelineSummaries[raReference] ??= { steps: event.ra[raReference], counts: counts.map(() => 0), + hash: getPipelineHash(event.ra[raReference]), }); const { counts: totalTuplesPerStep } = pipelineSummary; for (let i = 0, length = counts.length; i < length; ++i) { @@ -170,6 +204,25 @@ export class PerformanceOverviewScanner implements EvaluationLogScanner { totalTuplesPerStep[i] += count; } } + for (const dependencyHash of Object.values(event.dependencies ?? {})) { + const dependencyIndex = this.raToIndex.get(dependencyHash); + if (dependencyIndex != null) { + dependencyList.push(dependencyIndex); + } + } + // For predicates in the same SCC, add two-way dependencies with an arbitrary SCC member + const sccHash = + event.mainHash ?? + (evaluationStrategy === "COMPUTE_RECURSIVE" ? raHash : null); + if (sccHash != null) { + const mainIndex = this.mainHashToRepr.get(sccHash); + if (mainIndex == null) { + this.mainHashToRepr.set(sccHash, index); + } else { + dependencyLists[index].push(mainIndex); + dependencyLists[mainIndex].push(index); + } + } timeCosts[index] += totalTime; tupleCosts[index] += totalTuples; iterationCounts[index] += event.pipelineRuns?.length ?? 0; @@ -178,6 +231,12 @@ export class PerformanceOverviewScanner implements EvaluationLogScanner { } } } +} - onDone(): void {} +function getPipelineHash(steps: string[]) { + const md5 = createHash("md5"); + for (const step of steps) { + md5.write(step); + } + return md5.digest("base64"); } diff --git a/extensions/ql-vscode/src/log-insights/summary-language-support.ts b/extensions/ql-vscode/src/log-insights/summary-language-support.ts index 9919db114da..6546fd04ce2 100644 --- a/extensions/ql-vscode/src/log-insights/summary-language-support.ts +++ b/extensions/ql-vscode/src/log-insights/summary-language-support.ts @@ -113,7 +113,7 @@ export class SummaryLanguageSupport extends DisposableObject { const sourceMapText = await readFile(mapPath, "utf-8"); const rawMap: RawSourceMap = JSON.parse(sourceMapText); this.sourceMap = await new SourceMapConsumer(rawMap); - } catch (e: unknown) { + } catch (e) { // Error reading sourcemap. Pretend there was no sourcemap. void extLogger.log( `Error reading sourcemap file '${mapPath}': ${getErrorMessage(e)}`, diff --git a/extensions/ql-vscode/src/log-insights/summary-parser.ts b/extensions/ql-vscode/src/log-insights/summary-parser.ts index 6a42efca187..61a3a8af250 100644 --- a/extensions/ql-vscode/src/log-insights/summary-parser.ts +++ b/extensions/ql-vscode/src/log-insights/summary-parser.ts @@ -1,6 +1,3 @@ -import { createReadStream, writeFile } from "fs-extra"; -import { LINE_ENDINGS, splitStreamAtSeparators } from "../common/split-stream"; - /** * Location information for a single pipeline invocation in the RA. */ @@ -18,6 +15,11 @@ interface PredicateSymbol { * `PipelineInfo` for each iteration. A non-recursive predicate will have a single iteration `0`. */ iterations: Record; + + /** + * `PipelineInfo` for each order, summarised for all iterations that used that order. Empty for non-recursive predicates. + */ + recursionSummaries: Record; } /** @@ -27,102 +29,3 @@ interface PredicateSymbol { export interface SummarySymbols { predicates: Record; } - -// Tuple counts for Expr::Expr::getParent#dispred#f0820431#ff@76d6745o: -const NON_RECURSIVE_TUPLE_COUNT_REGEXP = - /^Evaluated relational algebra for predicate (?\S+) with tuple counts:$/; -// Tuple counts for Expr::Expr::getEnclosingStmt#f0820431#bf@923ddwj9 on iteration 0 running pipeline base: -const RECURSIVE_TUPLE_COUNT_REGEXP = - /^Evaluated relational algebra for predicate (?\S+) on iteration (?\d+) running pipeline (?\S+) with tuple counts:$/; -const RETURN_REGEXP = /^\s*return /; - -/** - * Parse a human-readable evaluation log summary to find the location of the RA for each pipeline - * run. - * - * TODO: Once we're more certain about the symbol format, we should have the CLI generate this as it - * generates the human-readabe summary to avoid having to rely on regular expression matching of the - * human-readable text. - * - * @param summaryPath The path to the summary file. - * @param symbolsPath The path to the symbols file to generate. - */ -export async function generateSummarySymbolsFile( - summaryPath: string, - symbolsPath: string, -): Promise { - const symbols = await generateSummarySymbols(summaryPath); - await writeFile(symbolsPath, JSON.stringify(symbols)); -} - -/** - * Parse a human-readable evaluation log summary to find the location of the RA for each pipeline - * run. - * - * @param fileLocation The path to the summary file. - * @returns Symbol information for the summary file. - */ -async function generateSummarySymbols( - summaryPath: string, -): Promise { - const stream = createReadStream(summaryPath, { - encoding: "utf-8", - }); - try { - const lines = splitStreamAtSeparators(stream, LINE_ENDINGS); - - const symbols: SummarySymbols = { - predicates: {}, - }; - - let lineNumber = 0; - let raStartLine = 0; - let iteration = 0; - let predicateName: string | undefined = undefined; - let startLine = 0; - for await (const line of lines) { - if (predicateName === undefined) { - // Looking for the start of the predicate. - const nonRecursiveMatch = line.match(NON_RECURSIVE_TUPLE_COUNT_REGEXP); - if (nonRecursiveMatch) { - iteration = 0; - predicateName = nonRecursiveMatch.groups!.predicateName; - } else { - const recursiveMatch = line.match(RECURSIVE_TUPLE_COUNT_REGEXP); - if (recursiveMatch?.groups) { - predicateName = recursiveMatch.groups.predicateName; - iteration = parseInt(recursiveMatch.groups.iteration); - } - } - if (predicateName !== undefined) { - startLine = lineNumber; - raStartLine = lineNumber + 1; - } - } else { - const returnMatch = line.match(RETURN_REGEXP); - if (returnMatch) { - let symbol = symbols.predicates[predicateName]; - if (symbol === undefined) { - symbol = { - iterations: {}, - }; - symbols.predicates[predicateName] = symbol; - } - symbol.iterations[iteration] = { - startLine, - raStartLine, - raEndLine: lineNumber, - }; - - predicateName = undefined; - } - } - - lineNumber++; - } - - return symbols; - } finally { - stream.close(); - } -} diff --git a/extensions/ql-vscode/src/model-editor/extension-pack-picker.ts b/extensions/ql-vscode/src/model-editor/extension-pack-picker.ts index 959a727e7e0..d5e8f0d78a5 100644 --- a/extensions/ql-vscode/src/model-editor/extension-pack-picker.ts +++ b/extensions/ql-vscode/src/model-editor/extension-pack-picker.ts @@ -115,7 +115,7 @@ export async function pickExtensionPack( existingExtensionPackPaths[0], databaseItem.language, ); - } catch (e: unknown) { + } catch (e) { void showAndLogErrorMessage( logger, `Could not read extension pack ${formatPackName(packName)}`, diff --git a/extensions/ql-vscode/src/model-editor/generate.ts b/extensions/ql-vscode/src/model-editor/generate.ts index 9f4b20c13da..157ed78b78d 100644 --- a/extensions/ql-vscode/src/model-editor/generate.ts +++ b/extensions/ql-vscode/src/model-editor/generate.ts @@ -91,6 +91,14 @@ async function runSingleGenerateQuery( if (!completedQuery) { return undefined; } + const queryResults = Array.from(completedQuery.results.values()); + if (queryResults.length !== 1) { + throw new Error( + `Expected exactly one query result, but got ${queryResults.length}`, + ); + } - return cliServer.bqrsDecodeAll(completedQuery.outputDir.bqrsPath); + return cliServer.bqrsDecodeAll( + completedQuery.outputDir.getBqrsPath(queryResults[0].outputBaseName), + ); } diff --git a/extensions/ql-vscode/src/model-editor/library.ts b/extensions/ql-vscode/src/model-editor/library.ts index bedf40f0786..7cc12514de9 100644 --- a/extensions/ql-vscode/src/model-editor/library.ts +++ b/extensions/ql-vscode/src/model-editor/library.ts @@ -32,7 +32,7 @@ export function parseLibraryFilename(filename: string): Library { semverRegex.lastIndex = 0; // Find the last occurence of the regex within the library name - // eslint-disable-next-line no-constant-condition + while (true) { const currentMatch = semverRegex.exec(libraryName); if (currentMatch === null) { diff --git a/extensions/ql-vscode/src/model-editor/model-editor-queries.ts b/extensions/ql-vscode/src/model-editor/model-editor-queries.ts index 0e7ddd48c4e..f1e7429afbd 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-queries.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-queries.ts @@ -172,10 +172,19 @@ export async function runModelEditorQueries( maxStep: externalApiQueriesProgressMaxStep, }); + const queryResults = Array.from(completedQuery.results.values()); + if (queryResults.length !== 1) { + throw new Error( + `Expected exactly one query result, but got ${queryResults.length}`, + ); + } + const bqrsChunk = await readQueryResults({ cliServer, logger, - bqrsPath: completedQuery.outputDir.bqrsPath, + bqrsPath: completedQuery.outputDir.getBqrsPath( + queryResults[0].outputBaseName, + ), }); if (!bqrsChunk) { return; diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 46422bf8b67..8f0ac3c75a0 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -455,7 +455,7 @@ export class ModelEditorView extends AbstractWebview< this.app.logger, ); this.modelingStore.setModeledMethods(this.databaseItem, modeledMethods); - } catch (e: unknown) { + } catch (e) { void showAndLogErrorMessage( this.app.logger, `Unable to read data extension YAML: ${getErrorMessage(e)}`, @@ -561,7 +561,7 @@ export class ModelEditorView extends AbstractWebview< t: "setAccessPathSuggestions", accessPathSuggestions: options, }); - } catch (e: unknown) { + } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, this.app.telemetry, @@ -605,7 +605,7 @@ export class ModelEditorView extends AbstractWebview< return; } - if (addedDatabase.language !== this.language) { + if ((addedDatabase.language as QueryLanguage) !== this.language) { void showAndLogErrorMessage( this.app.logger, `The selected database is for ${addedDatabase.language}, but the current database is for ${this.language}.`, @@ -645,7 +645,7 @@ export class ModelEditorView extends AbstractWebview< progress, token: this.cancellationTokenSource.token, }); - } catch (e: unknown) { + } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, this.app.telemetry, @@ -733,7 +733,7 @@ export class ModelEditorView extends AbstractWebview< progress, token: this.cancellationTokenSource.token, }); - } catch (e: unknown) { + } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, this.app.telemetry, diff --git a/extensions/ql-vscode/src/model-editor/suggestion-queries.ts b/extensions/ql-vscode/src/model-editor/suggestion-queries.ts index 37ce78fd3a5..f325fc2a4d5 100644 --- a/extensions/ql-vscode/src/model-editor/suggestion-queries.ts +++ b/extensions/ql-vscode/src/model-editor/suggestion-queries.ts @@ -109,7 +109,15 @@ export async function runSuggestionsQuery( maxStep, }); - const bqrs = await cliServer.bqrsDecodeAll(completedQuery.outputDir.bqrsPath); + const queryResults = Array.from(completedQuery.results.values()); + if (queryResults.length !== 1) { + throw new Error( + `Expected exactly one query result, but got ${queryResults.length}`, + ); + } + const bqrs = await cliServer.bqrsDecodeAll( + completedQuery.outputDir.getBqrsPath(queryResults[0].outputBaseName), + ); progress({ message: "Finalizing results", diff --git a/extensions/ql-vscode/src/query-history/history-item-label-provider.ts b/extensions/ql-vscode/src/query-history/history-item-label-provider.ts index 89f0f89a027..8787e025daf 100644 --- a/extensions/ql-vscode/src/query-history/history-item-label-provider.ts +++ b/extensions/ql-vscode/src/query-history/history-item-label-provider.ts @@ -115,7 +115,7 @@ export class HistoryItemLabelProvider { startTime: item.startTime, queryName: item.getQueryName(), databaseName: item.databaseName, - resultCount: `(${resultCount} results)`, + resultCount: resultCount === -1 ? "" : `(${resultCount} results)`, status: message, queryFileBasename: item.getQueryFileName(), queryLanguage: this.getLanguageLabel(item), diff --git a/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts b/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts index 8f63d109eb5..7b355539a9a 100644 --- a/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts +++ b/extensions/ql-vscode/src/query-history/history-tree-data-provider.ts @@ -138,12 +138,12 @@ export class HistoryTreeDataProvider const resultCount1 = h1.t === "local" - ? h1.completedQuery?.resultCount ?? -1 - : h1.resultCount ?? -1; + ? (h1.completedQuery?.resultCount ?? -1) + : (h1.resultCount ?? -1); const resultCount2 = h2.t === "local" - ? h2.completedQuery?.resultCount ?? -1 - : h2.resultCount ?? -1; + ? (h2.completedQuery?.resultCount ?? -1) + : (h2.resultCount ?? -1); switch (this.sortOrder) { case SortOrder.NameAsc: diff --git a/extensions/ql-vscode/src/query-history/query-history-manager.ts b/extensions/ql-vscode/src/query-history/query-history-manager.ts index 3074ceb3bb9..edf22f9b846 100644 --- a/extensions/ql-vscode/src/query-history/query-history-manager.ts +++ b/extensions/ql-vscode/src/query-history/query-history-manager.ts @@ -23,7 +23,8 @@ import { URLSearchParams } from "url"; import { DisposableObject } from "../common/disposable-object"; import { ONE_HOUR_IN_MS, TWO_HOURS_IN_MS } from "../common/time"; import { assertNever, getErrorMessage } from "../common/helpers-pure"; -import type { CompletedLocalQueryInfo, LocalQueryInfo } from "../query-results"; +import type { CompletedLocalQueryInfo } from "../query-results"; +import { LocalQueryInfo } from "../query-results"; import type { QueryHistoryInfo } from "./query-history-info"; import { getActionsWorkflowRunUrl, @@ -50,7 +51,10 @@ import type { QueryRunner } from "../query-server"; import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; import type { VariantAnalysisHistoryItem } from "./variant-analysis-history-item"; import { getTotalResultCount } from "../variant-analysis/shared/variant-analysis"; -import { HistoryTreeDataProvider } from "./history-tree-data-provider"; +import { + HistoryTreeDataProvider, + SortOrder, +} from "./history-tree-data-provider"; import type { QueryHistoryDirs } from "./query-history-dirs"; import type { QueryHistoryCommands } from "../common/commands"; import type { App } from "../common/app"; @@ -97,15 +101,6 @@ const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\ `; -enum SortOrder { - NameAsc = "NameAsc", - NameDesc = "NameDesc", - DateAsc = "DateAsc", - DateDesc = "DateDesc", - CountAsc = "CountAsc", - CountDesc = "CountDesc", -} - /** * Number of milliseconds two clicks have to arrive apart to be * considered a double-click. @@ -337,6 +332,11 @@ export class QueryHistoryManager extends DisposableObject { this.handleOpenOnGithub.bind(this), "query", ), + "codeQLQueryHistory.viewAutofixes": createSingleSelectionCommand( + this.app.logger, + this.handleViewAutofixes.bind(this), + "query", + ), "codeQLQueryHistory.copyRepoList": createSingleSelectionCommand( this.app.logger, this.handleCopyRepoList.bind(this), @@ -348,8 +348,37 @@ export class QueryHistoryManager extends DisposableObject { }; } - public completeQuery(info: LocalQueryInfo, results: QueryWithResults): void { - info.completeThisQuery(results); + public completeQueries( + info: LocalQueryInfo, + results: QueryWithResults[], + ): void { + let first = true; + // Sorting results by the output/results basename should produce a deterministic order. + results.sort((a, b) => { + const aPath = a.query.outputBaseName; + const bPath = b.query.outputBaseName; + return aPath.localeCompare(bPath); + }); + for (const result of results) { + if (first) { + // This is the first query, so we can just update the existing info. + info.completeThisQuery(result); + first = false; + } else { + // For other queries in the same run, we'll add new entries to the history pane. In the long + // term, it would be better if we could have a single entry containing sub-entries for each + // query. + const clonedInfo = new LocalQueryInfo( + info.initialInfo, + undefined, + info.failureReason, + undefined, + info.evaluatorLogPaths, + ); + clonedInfo.completeThisQuery(result); + this.addQuery(clonedInfo); + } + } this._onDidCompleteQuery.fire(info); } @@ -555,6 +584,23 @@ export class QueryHistoryManager extends DisposableObject { }), ); + await Promise.all( + this.treeDataProvider.allHistory.map(async (item) => { + // Remove any local queries whose directories no longer exist. This can happen when running + // a query suite, which produces multiple queries in the history pane that all share the + // same underlying directory, which we may have just deleted above. (Ideally, there would be + // a first-class concept of a local multi-query run in this pane that would group them all + // together, but doing it this way at least avoids cluttering the history pane with entries + // that can no longer be viewed). + if (item.t === "local") { + const dir = item.completedQuery?.query.querySaveDir; + if (dir && !(await pathExists(dir))) { + this.treeDataProvider.remove(item); + } + } + }), + ); + await this.writeQueryHistory(); const current = this.treeDataProvider.getCurrent(); if (current !== undefined) { @@ -942,7 +988,7 @@ export class QueryHistoryManager extends DisposableObject { if (hasInterpretedResults) { await tryOpenExternalFile( this.app.commands, - query.resultsPaths.interpretedResultsPath, + query.interpretedResultsPath, ); } else { const label = this.labelProvider.getLabel(item); @@ -1005,6 +1051,14 @@ export class QueryHistoryManager extends DisposableObject { ); } + async handleViewAutofixes(item: QueryHistoryInfo) { + if (item.t !== "variant-analysis") { + return; + } + + await this.variantAnalysisManager.viewAutofixes(item.variantAnalysis.id); + } + async handleCopyRepoList(item: QueryHistoryInfo) { if (item.t !== "variant-analysis") { return; diff --git a/extensions/ql-vscode/src/query-history/store/query-history-dto.ts b/extensions/ql-vscode/src/query-history/store/query-history-dto.ts index fd8bf01ccbc..78cb5b6064e 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-dto.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-dto.ts @@ -14,6 +14,7 @@ export type QueryHistoryItemDto = | QueryHistoryVariantAnalysisDto; export enum QueryLanguageDto { + Actions = "actions", CSharp = "csharp", Cpp = "cpp", Go = "go", diff --git a/extensions/ql-vscode/src/query-history/store/query-history-language-domain-mapper.ts b/extensions/ql-vscode/src/query-history/store/query-history-language-domain-mapper.ts index 615c820908b..1df15d41dd2 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-language-domain-mapper.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-language-domain-mapper.ts @@ -6,6 +6,8 @@ export function mapQueryLanguageToDto( language: QueryLanguage, ): QueryLanguageDto { switch (language) { + case QueryLanguage.Actions: + return QueryLanguageDto.Actions; case QueryLanguage.CSharp: return QueryLanguageDto.CSharp; case QueryLanguage.Cpp: diff --git a/extensions/ql-vscode/src/query-history/store/query-history-language-dto-mapper.ts b/extensions/ql-vscode/src/query-history/store/query-history-language-dto-mapper.ts index d4e0f03c1bf..1e3ea1c18a8 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-language-dto-mapper.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-language-dto-mapper.ts @@ -6,6 +6,8 @@ export function mapQueryLanguageToDomainModel( language: QueryLanguageDto, ): QueryLanguage { switch (language) { + case QueryLanguageDto.Actions: + return QueryLanguage.Actions; case QueryLanguageDto.CSharp: return QueryLanguage.CSharp; case QueryLanguageDto.Cpp: diff --git a/extensions/ql-vscode/src/query-history/store/query-history-local-query-domain-mapper.ts b/extensions/ql-vscode/src/query-history/store/query-history-local-query-domain-mapper.ts index 5f691e60785..61fe2e0bc4b 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-local-query-domain-mapper.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-local-query-domain-mapper.ts @@ -118,6 +118,6 @@ function mapQueryEvaluationInfoToDto( databaseHasMetadataFile: queryEvaluationInfo.databaseHasMetadataFile, quickEvalPosition: queryEvaluationInfo.quickEvalPosition, metadata: queryEvaluationInfo.metadata, - resultsPaths: queryEvaluationInfo.resultsPaths, + outputBaseName: queryEvaluationInfo.outputBaseName, }; } diff --git a/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto-mapper.ts b/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto-mapper.ts index 7afe4b907ad..aa42dd8c1a0 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto-mapper.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto-mapper.ts @@ -104,6 +104,7 @@ function mapQueryEvaluationInfoToDomainModel( ): QueryEvaluationInfo { return new QueryEvaluationInfo( evaluationInfo.querySaveDir, + evaluationInfo.outputBaseName ?? "results", evaluationInfo.dbItemPath, evaluationInfo.databaseHasMetadataFile, evaluationInfo.quickEvalPosition, diff --git a/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto.ts b/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto.ts index b9a2f3448fa..2a6b3c78ea0 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-local-query-dto.ts @@ -86,7 +86,10 @@ export interface QueryEvaluationInfoDto { databaseHasMetadataFile: boolean; quickEvalPosition?: PositionDto; metadata?: QueryMetadataDto; - resultsPaths: { + outputBaseName?: string; + + // Superceded by outputBaseName + resultsPaths?: { resultsPath: string; interpretedResultsPath: string; }; diff --git a/extensions/ql-vscode/src/query-history/store/query-history-store.ts b/extensions/ql-vscode/src/query-history/store/query-history-store.ts index 279c17c4dc0..0b54908ceba 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-store.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-store.ts @@ -61,7 +61,7 @@ export async function readQueryHistoryFromFile( // to see if they exist on disk. return true; } - const resultsPath = q.completedQuery?.query.resultsPaths.resultsPath; + const resultsPath = q.completedQuery?.query.resultsPath; return !!resultsPath && (await pathExists(resultsPath)); }, ); diff --git a/extensions/ql-vscode/src/query-results.ts b/extensions/ql-vscode/src/query-results.ts index 69a99837b52..3e81762bc08 100644 --- a/extensions/ql-vscode/src/query-results.ts +++ b/extensions/ql-vscode/src/query-results.ts @@ -64,7 +64,7 @@ export class CompletedQueryInfo implements QueryWithResults { * sarif file. */ public interpretedResultsSortState: InterpretedResultsSortState | undefined, - public resultCount: number = 0, + public resultCount: number = -1, /** * Map from result set name to SortedResultSetInfo. @@ -78,11 +78,11 @@ export class CompletedQueryInfo implements QueryWithResults { getResultsPath(selectedTable: string, useSorted = true): string { if (!useSorted) { - return this.query.resultsPaths.resultsPath; + return this.query.resultsPath; } return ( this.sortedResultsInfo[selectedTable]?.resultsPath || - this.query.resultsPaths.resultsPath + this.query.resultsPath ); } @@ -102,7 +102,7 @@ export class CompletedQueryInfo implements QueryWithResults { }; await server.sortBqrs( - this.query.resultsPaths.resultsPath, + this.query.resultsPath, sortedResultSetInfo.resultsPath, resultSetName, [sortState.columnIndex], diff --git a/extensions/ql-vscode/src/query-server/messages.ts b/extensions/ql-vscode/src/query-server/messages.ts index 44e0d515458..548e93ad32b 100644 --- a/extensions/ql-vscode/src/query-server/messages.ts +++ b/extensions/ql-vscode/src/query-server/messages.ts @@ -42,6 +42,22 @@ export interface TrimCacheParams { db: string; } +/** + * Parameters for trimming the cache of a dataset with a specific mode. + */ +export interface TrimCacheWithModeParams { + /** + * The dataset that we want to trim the cache of. + */ + db: string; + /** + * The cache cleanup mode to use. + */ + mode: ClearCacheMode; +} + +export type ClearCacheMode = "clear" | "trim" | "fit" | "overlay"; + /** * The result of trimming or clearing the cache. */ @@ -130,13 +146,29 @@ export interface RunQueryParams { extensionPacks?: string[]; } -interface RunQueryResult { +export interface RunQueryResult { resultType: QueryResultType; message?: string; expectedDbschemeName?: string; evaluationTime: number; } +export interface RunQueryInputOutput { + queryPath: string; + outputPath: string; + dilPath: string; +} + +export interface RunQueriesParams { + inputOutputPaths: RunQueryInputOutput[]; + db: string; + additionalPacks: string[]; + externalInputs: Record; + singletonExternalInputs: Record; + logPath?: string; + extensionPacks?: string[]; +} + interface UpgradeParams { db: string; additionalPacks: string[]; @@ -177,6 +209,14 @@ export const trimCache = new RequestType< ClearCacheResult, void >("evaluation/trimCache"); +/** + * Trim the cache of a dataset with a specific mode. + */ +export const trimCacheWithMode = new RequestType< + WithProgressId, + ClearCacheResult, + void +>("evaluation/trimCacheWithMode"); /** * Clear the pack cache @@ -196,6 +236,12 @@ export const runQuery = new RequestType< void >("evaluation/runQuery"); +export const runQueries = new RequestType< + WithProgressId, + Record, + void +>("evaluation/runQueries"); + export const registerDatabases = new RequestType< WithProgressId, RegisterDatabasesResult, diff --git a/extensions/ql-vscode/src/query-server/query-runner.ts b/extensions/ql-vscode/src/query-server/query-runner.ts index 7fbb3446575..5d42e6056d7 100644 --- a/extensions/ql-vscode/src/query-server/query-runner.ts +++ b/extensions/ql-vscode/src/query-server/query-runner.ts @@ -6,10 +6,12 @@ import { UserCancellationException } from "../common/vscode/progress"; import type { DatabaseItem } from "../databases/local-databases/database-item"; import { QueryOutputDir } from "../local-queries/query-output-dir"; import type { + ClearCacheMode, ClearCacheParams, Position, QueryResultType, TrimCacheParams, + TrimCacheWithModeParams, } from "./messages"; import { clearCache, @@ -17,21 +19,29 @@ import { deregisterDatabases, registerDatabases, trimCache, + trimCacheWithMode, upgradeDatabase, } from "./messages"; import type { BaseLogger, Logger } from "../common/logging"; -import { basename, join } from "path"; +import { join } from "path"; import { nanoid } from "nanoid"; import type { QueryServerClient } from "./query-server-client"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { compileAndRunQueryAgainstDatabaseCore } from "./run-queries"; export interface CoreQueryTarget { - /** The full path to the query. */ + /** Path to the query source file. */ queryPath: string; + + /** + * Base name to use for output files, without extension. For example, "foo" will result in the + * BQRS file being written to "/foo.bqrs". + */ + outputBaseName: string; + /** * Optional position of text to be used as QuickEval target. This need not be in the same file as - * `query`. + * `queryPath`. */ quickEvalPosition?: Position; /** @@ -40,14 +50,25 @@ export interface CoreQueryTarget { quickEvalCountOnly?: boolean; } -export interface CoreQueryResults { +export interface CoreQueryResult { readonly resultType: QueryResultType; readonly message: string | undefined; readonly evaluationTime: number; + + /** + * The base name of the output file. Append '.bqrs' and join with the output directory to get the + * path to the BQRS. + */ + readonly outputBaseName: string; +} + +export interface CoreQueryResults { + /** A map from query path to its results. */ + readonly results: Map; } export interface CoreQueryRun { - readonly queryTarget: CoreQueryTarget; + readonly queryTargets: CoreQueryTarget[]; readonly dbPath: string; readonly id: string; readonly outputDir: QueryOutputDir; @@ -124,9 +145,25 @@ export class QueryRunner { await this.qs.sendRequest(trimCache, params); } + async trimCacheWithModeInDatabase( + dbItem: DatabaseItem, + mode: ClearCacheMode, + ): Promise { + if (dbItem.contents === undefined) { + throw new Error("Can't clean the cache in an invalid database."); + } + + const db = dbItem.databaseUri.fsPath; + const params: TrimCacheWithModeParams = { + db, + mode, + }; + await this.qs.sendRequest(trimCacheWithMode, params); + } + public async compileAndRunQueryAgainstDatabaseCore( dbPath: string, - query: CoreQueryTarget, + queries: CoreQueryTarget[], additionalPacks: string[], extensionPacks: string[] | undefined, additionalRunQueryArgs: Record, @@ -142,7 +179,7 @@ export class QueryRunner { return await compileAndRunQueryAgainstDatabaseCore( this.qs, dbPath, - query, + queries, generateEvalLog, additionalPacks, extensionPacks, @@ -213,19 +250,20 @@ export class QueryRunner { */ public createQueryRun( dbPath: string, - query: CoreQueryTarget, + queries: CoreQueryTarget[], generateEvalLog: boolean, additionalPacks: string[], extensionPacks: string[] | undefined, additionalRunQueryArgs: Record, queryStorageDir: string, - id = `${basename(query.queryPath)}-${nanoid()}`, + queryBasename: string, templates: Record | undefined, ): CoreQueryRun { + const id = `${queryBasename}-${nanoid()}`; const outputDir = new QueryOutputDir(join(queryStorageDir, id)); return { - queryTarget: query, + queryTargets: queries, dbPath, id, outputDir, @@ -238,10 +276,10 @@ export class QueryRunner { id, outputDir, dbPath, - queryTarget: query, + queryTargets: queries, ...(await this.compileAndRunQueryAgainstDatabaseCore( dbPath, - query, + queries, additionalPacks, extensionPacks, additionalRunQueryArgs, diff --git a/extensions/ql-vscode/src/query-server/query-server-client.ts b/extensions/ql-vscode/src/query-server/query-server-client.ts index c342e3b4996..e62637ff3ad 100644 --- a/extensions/ql-vscode/src/query-server/query-server-client.ts +++ b/extensions/ql-vscode/src/query-server/query-server-client.ts @@ -95,6 +95,14 @@ export class QueryServerClient extends DisposableObject { return this.opts.logger; } + /** + * Whether this query server supports the 'evaluation/runQueries' method for running multiple + * queries at once. + */ + async supportsRunQueriesMethod(): Promise { + return await this.cliServer.cliConstraints.supportsQueryServerRunQueries(); + } + /** Stops the query server by disposing of the current server process. */ private stopQueryServer(): void { if (this.serverProcess !== undefined) { @@ -296,7 +304,7 @@ export class QueryServerClient extends DisposableObject { } } - public dispose(disposeHandler?: DisposeHandler | undefined): void { + public dispose(disposeHandler?: DisposeHandler): void { this.progressCallbacks = {}; this.stopQueryServer(); super.dispose(disposeHandler); diff --git a/extensions/ql-vscode/src/query-server/run-queries.ts b/extensions/ql-vscode/src/query-server/run-queries.ts index 593979118e7..5a35144728f 100644 --- a/extensions/ql-vscode/src/query-server/run-queries.ts +++ b/extensions/ql-vscode/src/query-server/run-queries.ts @@ -1,10 +1,19 @@ import type { CancellationToken } from "vscode"; import type { ProgressCallback } from "../common/vscode/progress"; -import type { RunQueryParams } from "./messages"; -import { runQuery } from "./messages"; +import type { + RunQueryParams, + RunQueryResult, + RunQueriesParams, + RunQueryInputOutput, +} from "./messages"; +import { runQueries, runQuery } from "./messages"; import type { QueryOutputDir } from "../local-queries/query-output-dir"; import type { QueryServerClient } from "./query-server-client"; -import type { CoreQueryResults, CoreQueryTarget } from "./query-runner"; +import type { + CoreQueryResult, + CoreQueryResults, + CoreQueryTarget, +} from "./query-runner"; import type { BaseLogger } from "../common/logging"; /** @@ -24,7 +33,7 @@ import type { BaseLogger } from "../common/logging"; export async function compileAndRunQueryAgainstDatabaseCore( qs: QueryServerClient, dbPath: string, - query: CoreQueryTarget, + targets: CoreQueryTarget[], generateEvalLog: boolean, additionalPacks: string[], extensionPacks: string[] | undefined, @@ -35,12 +44,36 @@ export async function compileAndRunQueryAgainstDatabaseCore( templates: Record | undefined, logger: BaseLogger, ): Promise { - const target = - query.quickEvalPosition !== undefined + if (targets.length > 1) { + // We are running a batch of multiple queries; use the new query server API for that. + if (targets.some((target) => target.quickEvalPosition !== undefined)) { + throw new Error( + "Quick evaluation is not supported when running multiple queries.", + ); + } + return compileAndRunQueriesAgainstDatabaseCore( + qs, + dbPath, + targets, + generateEvalLog, + additionalPacks, + extensionPacks, + additionalRunQueryArgs, + outputDir, + progress, + token, + templates, + logger, + ); + } + + const target = targets[0]; + const compilationTarget = + target.quickEvalPosition !== undefined ? { quickEval: { - quickEvalPos: query.quickEvalPosition, - countOnly: query.quickEvalCountOnly, + quickEvalPos: target.quickEvalPosition, + countOnly: target.quickEvalCountOnly, }, } : { query: {} }; @@ -51,11 +84,11 @@ export async function compileAndRunQueryAgainstDatabaseCore( additionalPacks, externalInputs: {}, singletonExternalInputs: templates || {}, - outputPath: outputDir.bqrsPath, - queryPath: query.queryPath, - dilPath: outputDir.dilPath, + queryPath: target.queryPath, + outputPath: outputDir.getBqrsPath(target.outputBaseName), + dilPath: outputDir.getDilPath(target.outputBaseName), logPath: evalLogPath, - target, + target: compilationTarget, extensionPacks, // Add any additional arguments without interpretation. ...additionalRunQueryArgs, @@ -67,10 +100,83 @@ export async function compileAndRunQueryAgainstDatabaseCore( // properly will require a change in the query server. qs.activeQueryLogger = logger; const result = await qs.sendRequest(runQuery, queryToRun, token, progress); + return { + results: new Map([ + [ + target.queryPath, + { + resultType: result.resultType, + message: result.message, + evaluationTime: result.evaluationTime, + outputBaseName: target.outputBaseName, + }, + ], + ]), + }; +} + +async function compileAndRunQueriesAgainstDatabaseCore( + qs: QueryServerClient, + dbPath: string, + targets: CoreQueryTarget[], + generateEvalLog: boolean, + additionalPacks: string[], + extensionPacks: string[] | undefined, + additionalRunQueryArgs: Record, + outputDir: QueryOutputDir, + progress: ProgressCallback, + token: CancellationToken, + templates: Record | undefined, + logger: BaseLogger, +): Promise { + if (!(await qs.supportsRunQueriesMethod())) { + throw new Error( + "The CodeQL CLI does not support the 'evaluation/runQueries' query-server command. Please update to the latest version.", + ); + } + const inputOutputPaths: RunQueryInputOutput[] = targets.map((target) => { + return { + queryPath: target.queryPath, + outputPath: outputDir.getBqrsPath(target.outputBaseName), + dilPath: outputDir.getDilPath(target.outputBaseName), + }; + }); + const evalLogPath = generateEvalLog ? outputDir.evalLogPath : undefined; + const queriesToRun: RunQueriesParams = { + db: dbPath, + additionalPacks, + externalInputs: {}, + singletonExternalInputs: templates || {}, + inputOutputPaths, + logPath: evalLogPath, + extensionPacks, + // Add any additional arguments without interpretation. + ...additionalRunQueryArgs, + }; + + // Update the active query logger every time there is a new request to compile. + // This isn't ideal because in situations where there are queries running + // in parallel, each query's log messages are interleaved. Fixing this + // properly will require a change in the query server. + qs.activeQueryLogger = logger; + const queryResults: Record = await qs.sendRequest( + runQueries, + queriesToRun, + token, + progress, + ); + const coreQueryResults = new Map(); + targets.forEach((target) => { + const queryResult = queryResults[target.queryPath]; + coreQueryResults.set(target.queryPath, { + resultType: queryResult.resultType, + message: queryResult.message, + evaluationTime: queryResult.evaluationTime, + outputBaseName: target.outputBaseName, + }); + }); return { - resultType: result.resultType, - message: result.message, - evaluationTime: result.evaluationTime, + results: coreQueryResults, }; } diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index dac447ee20b..4e463812cb0 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -27,7 +27,6 @@ import type { import type { BaseLogger } from "./common/logging"; import { showAndLogWarningMessage } from "./common/logging"; import { extLogger } from "./common/logging/vscode"; -import { generateSummarySymbolsFile } from "./log-insights/summary-parser"; import { getErrorMessage } from "./common/helpers-pure"; import { createHash } from "crypto"; import { QueryOutputDir } from "./local-queries/query-output-dir"; @@ -65,6 +64,7 @@ export class QueryEvaluationInfo extends QueryOutputDir { */ constructor( querySaveDir: string, + public readonly outputBaseName: string, public readonly dbItemPath: string, public readonly databaseHasMetadataFile: boolean, public readonly quickEvalPosition?: Position, @@ -73,23 +73,30 @@ export class QueryEvaluationInfo extends QueryOutputDir { super(querySaveDir); } - get resultsPaths() { - return { - resultsPath: this.bqrsPath, - interpretedResultsPath: join( - this.querySaveDir, - this.metadata?.kind === "graph" - ? "graphResults" - : "interpretedResults.sarif", - ), - }; + get resultsPath() { + return this.getBqrsPath(this.outputBaseName); } + + get interpretedResultsPath() { + return this.getInterpretedResultsPath( + this.metadata?.kind, + this.outputBaseName, + ); + } + + get csvPath() { + return this.getCsvPath(this.outputBaseName); + } + + get dilPath() { + return this.getDilPath(this.outputBaseName); + } + getSortedResultSetPath(resultSetName: string) { const hasher = createHash("sha256"); hasher.update(resultSetName); - return join( - this.querySaveDir, - `sortedResults-${hasher.digest("hex")}.bqrs`, + return this.getBqrsPath( + `${this.outputBaseName}-sorted-${hasher.digest("hex")}`, ); } @@ -127,7 +134,7 @@ export class QueryEvaluationInfo extends QueryOutputDir { * Holds if this query actually has produced interpreted results. */ async hasInterpretedResults(): Promise { - return pathExists(this.resultsPaths.interpretedResultsPath); + return pathExists(this.interpretedResultsPath); } /** @@ -205,7 +212,7 @@ export class QueryEvaluationInfo extends QueryOutputDir { let nextOffset: number | undefined = 0; do { const chunk: DecodedBqrsChunk = await cliServer.bqrsDecode( - this.resultsPaths.resultsPath, + this.resultsPath, resultSet, { pageSize: 100, @@ -243,9 +250,9 @@ export class QueryEvaluationInfo extends QueryOutputDir { * If the query has no result sets, then return undefined. */ async chooseResultSet(cliServer: CodeQLCliServer) { - const resultSets = ( - await cliServer.bqrsInfo(this.resultsPaths.resultsPath) - )["result-sets"]; + const resultSets = (await cliServer.bqrsInfo(this.resultsPath))[ + "result-sets" + ]; if (!resultSets.length) { return undefined; } @@ -284,7 +291,7 @@ export class QueryEvaluationInfo extends QueryOutputDir { } await cliServer.generateResultsCsv( ensureMetadataIsComplete(this.metadata), - this.resultsPaths.resultsPath, + this.resultsPath, this.csvPath, sourceInfo, ); @@ -348,6 +355,23 @@ export function validateQueryPath( } } +/** + * Validates that the specified URI represents a QL query suite (QLS), and returns the file system + * path to that suite. + */ +export function validateQuerySuiteUri(suiteUri: Uri): string { + if (suiteUri.scheme !== "file") { + throw new Error("Can only run queries that are on disk."); + } + const suitePath = suiteUri.fsPath; + if (!suitePath.endsWith(".qls")) { + throw new Error( + 'The selected resource is not a CodeQL query suite; It should have the extension ".qls".', + ); + } + return suitePath; +} + export interface QuickEvalContext { quickEvalPosition: Position; quickEvalText: string; @@ -545,15 +569,6 @@ export async function generateEvalLogSummaries( if (humanReadableSummary !== undefined) { summarySymbols = outputDir.evalLogSummarySymbolsPath; - if ( - !(await cliServer.cliConstraints.supportsGenerateSummarySymbolMap()) - ) { - // We're using an old CLI that cannot generate the summary symbols file while generating the - // human-readable log summary. As a fallback, create it by parsing the human-readable - // summary. - progress(progressUpdate(3, 3, "Generating summary symbols file")); - await generateSummarySymbolsFile(humanReadableSummary, summarySymbols); - } } } diff --git a/extensions/ql-vscode/src/stories/Overview.mdx b/extensions/ql-vscode/src/stories/Overview.mdx index cc06ee5cfca..7d9939cb4d9 100644 --- a/extensions/ql-vscode/src/stories/Overview.mdx +++ b/extensions/ql-vscode/src/stories/Overview.mdx @@ -1,7 +1,5 @@ import { Canvas, Meta, Story } from '@storybook/blocks'; -import { VSCodeButton } from '@vscode/webview-ui-toolkit/react'; - import iframeImage from './images/update-css-variables-iframe.png'; import stylesImage from './images/update-css-variables-styles.png'; import bodyImage from './images/update-css-variables-body.png'; diff --git a/extensions/ql-vscode/src/stories/common/Alert.stories.tsx b/extensions/ql-vscode/src/stories/common/Alert.stories.tsx index 1a5db4f14f6..2267ccb2696 100644 --- a/extensions/ql-vscode/src/stories/common/Alert.stories.tsx +++ b/extensions/ql-vscode/src/stories/common/Alert.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryFn } from "@storybook/react"; -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; +import { VscodeButton } from "@vscode-elements/react-elements"; import { VariantAnalysisContainer } from "../../view/variant-analysis/VariantAnalysisContainer"; import { Alert } from "../../view/common"; @@ -84,8 +84,8 @@ ErrorWithButtons.args = { "Request to https://api.github.com/repos/octodemo/Hello-World/code-scanning/codeql/queries failed. Try running this query again.", actions: ( <> - View actions logs - Retry + View actions logs + Retry ), }; diff --git a/extensions/ql-vscode/src/stories/model-editor/LibraryRow.stories.tsx b/extensions/ql-vscode/src/stories/model-editor/LibraryRow.stories.tsx index 423a948cd0e..16cb303b786 100644 --- a/extensions/ql-vscode/src/stories/model-editor/LibraryRow.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-editor/LibraryRow.stories.tsx @@ -220,6 +220,7 @@ LibraryRow.args = { ], }, modifiedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]), + selectedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]), viewState: createMockModelEditorViewState({ showGenerateButton: true, }), diff --git a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx index 6b10353d0a9..f7bf0748b20 100644 --- a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx @@ -112,6 +112,7 @@ Source.args = { modeledMethods: [{ ...modeledMethod, type: "source" }], methodCanBeModeled: true, viewState, + onChange: () => {}, }; export const Sink = Template.bind({}); @@ -120,6 +121,7 @@ Sink.args = { modeledMethods: [{ ...modeledMethod, type: "sink" }], methodCanBeModeled: true, viewState, + onChange: () => {}, }; export const Summary = Template.bind({}); @@ -136,6 +138,7 @@ Neutral.args = { modeledMethods: [{ ...modeledMethod, type: "neutral" }], methodCanBeModeled: true, viewState, + onChange: () => {}, }; export const AlreadyModeled = Template.bind({}); @@ -155,6 +158,7 @@ MultipleModelings.args = { ], methodCanBeModeled: true, viewState, + onChange: () => {}, }; export const ValidationError = Template.bind({}); @@ -166,6 +170,7 @@ ValidationError.args = { ], methodCanBeModeled: true, viewState, + onChange: () => {}, }; export const MultipleValidationErrors = Template.bind({}); @@ -180,4 +185,5 @@ MultipleValidationErrors.args = { ], methodCanBeModeled: true, viewState, + onChange: () => {}, }; diff --git a/extensions/ql-vscode/src/stories/results/AlertTable.stories.tsx b/extensions/ql-vscode/src/stories/results/AlertTable.stories.tsx index bfb179a1fed..d2cc19f3916 100644 --- a/extensions/ql-vscode/src/stories/results/AlertTable.stories.tsx +++ b/extensions/ql-vscode/src/stories/results/AlertTable.stories.tsx @@ -431,4 +431,7 @@ WithCodeFlows.args = { showRawResults={() => action("show-raw-results")} /> ), + userSettings: { + shouldShowProvenance: true, + }, }; diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index c49d9d3f27e..5b29eed8fd0 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -1,10 +1,9 @@ import type { CancellationToken } from "vscode"; -import { Uri, window } from "vscode"; -import { join, sep, basename, relative } from "path"; -import { dump, load } from "js-yaml"; -import { copy, writeFile, readFile, mkdirp } from "fs-extra"; -import type { DirectoryResult } from "tmp-promise"; -import { dir, tmpName } from "tmp-promise"; +import { window } from "vscode"; +import { join, basename, relative } from "path"; +import { dump } from "js-yaml"; +import { copy, writeFile, readFile, mkdirp, remove } from "fs-extra"; +import { nanoid } from "nanoid"; import { tmpDir } from "../tmp-dir"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import type { Credentials } from "../common/authentication"; @@ -28,13 +27,7 @@ import { } from "./repository-selection"; import type { Repository } from "./shared/repository"; import type { DbManager } from "../databases/db-manager"; -import { - getQlPackFilePath, - FALLBACK_QLPACK_FILENAME, - QLPACK_FILENAMES, - QLPACK_LOCK_FILENAMES, -} from "../common/ql"; -import type { QlPackFile } from "../packaging/qlpack-file"; +import { FALLBACK_QLPACK_FILENAME } from "../common/ql"; import { expandShortPaths } from "../common/short-paths"; import type { QlPackDetails } from "./ql-pack-details"; import type { ModelPackDetails } from "../common/model-pack-details"; @@ -69,8 +62,6 @@ async function generateQueryPack( ); const mustSynthesizePack = qlPackDetails.qlPackFilePath === undefined; - const cliSupportsMrvaPackCreate = - await cliServer.cliConstraints.supportsMrvaPackCreate(); let targetPackPath: string; let needsInstall: boolean; @@ -88,15 +79,6 @@ async function generateQueryPack( // Install packs, since we just synthesized a dependency on the language's standard library. needsInstall = true; - } else if (!cliSupportsMrvaPackCreate) { - // We need to copy the query pack to a temporary directory and then fix it up to work with MRVA. - targetPackPath = tmpDir.queryPackDir; - await copyExistingQueryPack(cliServer, qlPackDetails, targetPackPath); - - // We should already have all the dependencies available, but these older versions of the CLI - // have a bug where they will not search `--additional-packs` during validation in `codeql pack bundle`. - // Installing the packs will ensure that any extension packs get put in the right place. - needsInstall = true; } else { // The CLI supports creating a MRVA query pack directly from the source pack. targetPackPath = qlPackDetails.qlPackRootPath; @@ -114,27 +96,18 @@ async function generateQueryPack( await cliServer.clearCache(); } - let precompilationOpts: string[]; - if (cliSupportsMrvaPackCreate) { - const queryOpts = qlPackDetails.queryFiles.flatMap((q) => [ - "--query", - join(targetPackPath, relative(qlPackDetails.qlPackRootPath, q)), - ]); - - precompilationOpts = [ - "--mrva", - ...queryOpts, - // We need to specify the extension packs as dependencies so that they are included in the MRVA pack. - // The version range doesn't matter, since they'll always be found by source lookup. - ...extensionPacks.map((p) => `--extension-pack=${p.name}@*`), - ]; - } else { - precompilationOpts = ["--qlx"]; - - if (extensionPacks.length > 0) { - await addExtensionPacksAsDependencies(targetPackPath, extensionPacks); - } - } + const queryOpts = qlPackDetails.queryFiles.flatMap((q) => [ + "--query", + join(targetPackPath, relative(qlPackDetails.qlPackRootPath, q)), + ]); + + const precompilationOpts = [ + "--mrva", + ...queryOpts, + // We need to specify the extension packs as dependencies so that they are included in the MRVA pack. + // The version range doesn't matter, since they'll always be found by source lookup. + ...extensionPacks.map((p) => `--extension-pack=${p.name}@*`), + ]; const bundlePath = tmpDir.bundleFile; void extLogger.log( @@ -182,93 +155,29 @@ async function createNewQueryPack( ); } -async function copyExistingQueryPack( - cliServer: CodeQLCliServer, - qlPackDetails: QlPackDetails, - targetPackPath: string, -) { - const toCopy = await cliServer.packPacklist( - qlPackDetails.qlPackRootPath, - false, - ); - - // Also include query files that contain extensible predicates. These query files are not - // needed for the query to run, but they are needed for the query pack to pass deep validation - // of data extensions. - const metadata = await cliServer.generateExtensiblePredicateMetadata( - qlPackDetails.qlPackRootPath, - ); - metadata.extensible_predicates.forEach((predicate) => { - if (predicate.path.endsWith(".ql")) { - toCopy.push(join(qlPackDetails.qlPackRootPath, predicate.path)); - } - }); - - [ - // also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist. - ...QLPACK_LOCK_FILENAMES.map((f) => join(qlPackDetails.qlPackRootPath, f)), - ...qlPackDetails.queryFiles, - ].forEach((absolutePath) => { - if (absolutePath) { - toCopy.push(absolutePath); - } - }); - - let copiedCount = 0; - await copy(qlPackDetails.qlPackRootPath, targetPackPath, { - filter: (file: string) => - // copy file if it is in the packlist, or it is a parent directory of a file in the packlist - !!toCopy.find((f) => { - // Normalized paths ensure that Windows drive letters are capitalized consistently. - const normalizedPath = Uri.file(f).fsPath; - const matches = - normalizedPath === file || normalizedPath.startsWith(file + sep); - if (matches) { - copiedCount++; - } - return matches; - }), - }); - - void extLogger.log(`Copied ${copiedCount} files to ${targetPackPath}`); - - await fixPackFile(targetPackPath, qlPackDetails); -} - interface RemoteQueryTempDir { - remoteQueryDir: DirectoryResult; + remoteQueryDir: string; queryPackDir: string; compiledPackDir: string; bundleFile: string; } async function createRemoteQueriesTempDirectory(): Promise { - const shortRemoteQueryDir = await dir({ - dir: tmpDir.name, - unsafeCleanup: true, - }); // Expand 8.3 filenames here to work around a CLI bug where `codeql pack bundle` produces an empty // archive if the pack path contains any 8.3 components. - const remoteQueryDir = { - ...shortRemoteQueryDir, - path: await expandShortPaths(shortRemoteQueryDir.path, extLogger), - }; - const queryPackDir = join(remoteQueryDir.path, "query-pack"); + const tmpDirPath = await expandShortPaths(tmpDir.name, extLogger); + + const remoteQueryDir = join(tmpDirPath, `remote-query-${nanoid()}`); + await mkdirp(remoteQueryDir); + + const queryPackDir = join(remoteQueryDir, "query-pack"); await mkdirp(queryPackDir); - const compiledPackDir = join(remoteQueryDir.path, "compiled-pack"); - const bundleFile = await expandShortPaths( - await getPackedBundlePath(tmpDir.name), - extLogger, - ); - return { remoteQueryDir, queryPackDir, compiledPackDir, bundleFile }; -} -async function getPackedBundlePath(remoteQueryDir: string): Promise { - return tmpName({ - dir: remoteQueryDir, - postfix: "generated.tgz", - prefix: "qlpack", - }); + const compiledPackDir = join(remoteQueryDir, "compiled-pack"); + + const bundleFile = join(remoteQueryDir, `qlpack-${nanoid()}-generated.tgz`); + + return { remoteQueryDir, queryPackDir, compiledPackDir, bundleFile }; } interface PreparedRemoteQuery { @@ -337,7 +246,7 @@ export async function prepareRemoteQueryRun( token, ); } finally { - await tempDir.remoteQueryDir.cleanup(); + await remove(tempDir.remoteQueryDir); } if (token.isCancellationRequested) { @@ -363,42 +272,6 @@ export async function prepareRemoteQueryRun( }; } -/** - * Fixes the qlpack.yml or codeql-pack.yml file to be correct in the context of the MRVA request. - * - * Performs the following fixes: - * - * - Updates the default suite of the query pack. This is used to ensure - * only the specified query is run. - * - Ensures the query pack name is set to the name expected by the server. - * - Removes any `${workspace}` version references from the qlpack.yml or codeql-pack.yml file. Converts them - * to `*` versions. - * - * @param targetPackPath The path to the directory containing the target pack - * @param qlPackDetails The details of the original QL pack - */ -async function fixPackFile( - targetPackPath: string, - qlPackDetails: QlPackDetails, -): Promise { - const packPath = await getQlPackFilePath(targetPackPath); - - // This should not happen since we create the pack ourselves. - if (!packPath) { - throw new Error( - `Could not find ${QLPACK_FILENAMES.join( - " or ", - )} file in '${targetPackPath}'`, - ); - } - const qlpack = load(await readFile(packPath, "utf8")) as QlPackFile; - - updateDefaultSuite(qlpack, qlPackDetails); - removeWorkspaceRefs(qlpack); - - await writeFile(packPath, dump(qlpack)); -} - async function getExtensionPacksToInject( cliServer: CodeQLCliServer, workspaceFolders: string[], @@ -427,41 +300,6 @@ async function getExtensionPacksToInject( return result; } -async function addExtensionPacksAsDependencies( - queryPackDir: string, - extensionPacks: ModelPackDetails[], -): Promise { - const qlpackFile = await getQlPackFilePath(queryPackDir); - if (!qlpackFile) { - throw new Error( - `Could not find ${QLPACK_FILENAMES.join( - " or ", - )} file in '${queryPackDir}'`, - ); - } - - const syntheticQueryPack = load( - await readFile(qlpackFile, "utf8"), - ) as QlPackFile; - - const dependencies = syntheticQueryPack.dependencies ?? {}; - extensionPacks.forEach(({ name }) => { - // Add this extension pack as a dependency. It doesn't matter which - // version we specify, since we are guaranteed that the extension pack - // is resolved from source at the given path. - dependencies[name] = "*"; - }); - - syntheticQueryPack.dependencies = dependencies; - - await writeFile(qlpackFile, dump(syntheticQueryPack)); -} - -function updateDefaultSuite(qlpack: QlPackFile, qlPackDetails: QlPackDetails) { - delete qlpack.defaultSuiteFile; - qlpack.defaultSuite = generateDefaultSuite(qlPackDetails); -} - function generateDefaultSuite(qlPackDetails: QlPackDetails) { const queries = qlPackDetails.queryFiles.map((query) => { const relativePath = relative(qlPackDetails.qlPackRootPath, query); @@ -562,15 +400,3 @@ async function getControllerRepoFromApi( } } } - -function removeWorkspaceRefs(qlpack: QlPackFile) { - if (!qlpack.dependencies) { - return; - } - - for (const [key, value] of Object.entries(qlpack.dependencies)) { - if (value === "${workspace}") { - qlpack.dependencies[key] = "*"; - } - } -} diff --git a/extensions/ql-vscode/src/variant-analysis/sarif-processing.ts b/extensions/ql-vscode/src/variant-analysis/sarif-processing.ts index aa643d9f576..728fc90d41f 100644 --- a/extensions/ql-vscode/src/variant-analysis/sarif-processing.ts +++ b/extensions/ql-vscode/src/variant-analysis/sarif-processing.ts @@ -67,7 +67,7 @@ function extractResultAlerts( const rule = tryGetRule(run, result); const severity = tryGetSeverity(run, result, rule) || defaultSeverity; const codeFlows = getCodeFlows(result, fileLinkPrefix); - const shortDescription = getShortDescription(rule, message!); + const shortDescription = getShortDescription(rule, message); for (const location of result.locations ?? []) { const physicalLocation = location.physicalLocation!; @@ -266,7 +266,7 @@ function getCodeFlows(result: Result, fileLinkPrefix: string): CodeFlow[] { for (const threadFlow of codeFlow.threadFlows) { for (const threadFlowLocation of threadFlow.locations) { const physicalLocation = - threadFlowLocation!.location!.physicalLocation!; + threadFlowLocation.location!.physicalLocation!; const filePath = tryGetFilePath(physicalLocation); const codeSnippet = getCodeSnippet( physicalLocation.contextRegion, @@ -299,7 +299,7 @@ function getCodeFlows(result: Result, fileLinkPrefix: string): CodeFlow[] { function getMessage(result: Result, fileLinkPrefix: string): AnalysisMessage { const tokens: AnalysisMessageToken[] = []; - const messageText = result.message!.text!; + const messageText = result.message.text!; const messageParts = parseSarifPlainTextMessage(messageText); for (const messagePart of messageParts) { diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts index 2bb3a60e924..90f09ba15e8 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -34,7 +34,7 @@ export interface VariantAnalysis { export function parseVariantAnalysisQueryLanguage( language: string, ): QueryLanguage | undefined { - return Object.values(QueryLanguage).find((x) => x === language); + return Object.values(QueryLanguage).find((x) => x.toString() === language); } export enum VariantAnalysisStatus { diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 4e56c3cea7f..9bb707a9775 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -73,6 +73,7 @@ import type { VariantAnalysisCommands, } from "../common/commands"; import { exportVariantAnalysisResults } from "./export-results"; +import { viewAutofixesForVariantAnalysisResults } from "./view-autofixes"; import { readRepoStates, REPO_STATES_FILENAME, @@ -424,7 +425,7 @@ export class VariantAnalysisManager this.app.credentials, variantAnalysisSubmission, ); - } catch (e: unknown) { + } catch (e) { // If the error is handled by the handleRequestError function, we don't need to throw if ( e instanceof RequestError && @@ -967,6 +968,21 @@ export class VariantAnalysisManager ); } + public async viewAutofixes( + variantAnalysisId: number, + filterSort: RepositoriesFilterSortStateWithIds = defaultFilterSortState, + ) { + await viewAutofixesForVariantAnalysisResults( + this, + this.variantAnalysisResultsManager, + variantAnalysisId, + filterSort, + this.app.credentials, + this.app, + this.cliServer, + ); + } + public async copyRepoListToClipboard( variantAnalysisId: number, filterSort: RepositoriesFilterSortStateWithIds = defaultFilterSortState, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index c3a72a26705..2b8858035a2 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -76,15 +76,11 @@ function mapVariantAnalysis( let skippedRepos: VariantAnalysisSkippedRepositories = {}; if (response.scanned_repositories) { - scannedRepos = mapScannedRepositories( - response.scanned_repositories as ApiVariantAnalysisScannedRepository[], - ); + scannedRepos = mapScannedRepositories(response.scanned_repositories); } if (response.skipped_repositories) { - skippedRepos = mapSkippedRepositories( - response.skipped_repositories as ApiVariantAnalysisSkippedRepositories, - ); + skippedRepos = mapSkippedRepositories(response.skipped_repositories); } // Maintain the canceling status if we are still canceling. diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts index 0f24e950325..01e27410ff0 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-results-manager.ts @@ -44,6 +44,7 @@ export type LoadResultsOptions = { export class VariantAnalysisResultsManager extends DisposableObject { private static readonly RESULTS_DIRECTORY = "results"; + private static readonly RESULTS_SARIF_FILENAME = "results.sarif"; private readonly cachedResults: Map< CacheKey, @@ -212,7 +213,10 @@ export class VariantAnalysisResultsManager extends DisposableObject { storageDirectory, VariantAnalysisResultsManager.RESULTS_DIRECTORY, ); - const sarifPath = join(resultsDirectory, "results.sarif"); + const sarifPath = join( + resultsDirectory, + VariantAnalysisResultsManager.RESULTS_SARIF_FILENAME, + ); const bqrsPath = join(resultsDirectory, "results.bqrs"); let interpretedResults: AnalysisAlert[] | undefined; @@ -294,6 +298,17 @@ export class VariantAnalysisResultsManager extends DisposableObject { return join(variantAnalysisStoragePath, fullName); } + public getRepoResultsSarifStoragePath( + variantAnalysisStoragePath: string, + fullName: string, + ): string { + return join( + this.getRepoStorageDirectory(variantAnalysisStoragePath, fullName), + VariantAnalysisResultsManager.RESULTS_DIRECTORY, + VariantAnalysisResultsManager.RESULTS_SARIF_FILENAME, + ); + } + private createGitHubFileLinkPrefix(fullName: string, sha: string): string { return new URL( `/${fullName}/blob/${sha}`, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts index cc531c558e7..db591ac3d95 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts @@ -34,4 +34,8 @@ export interface VariantAnalysisViewManager< variantAnalysisId: number, filterSort?: RepositoriesFilterSortStateWithIds, ): Promise; + viewAutofixes( + variantAnalysisId: number, + filterSort?: RepositoriesFilterSortStateWithIds, + ): Promise; } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts index f8a87e47b87..555a3686e68 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts @@ -27,6 +27,7 @@ import type { App } from "../common/app"; import { getVariantAnalysisDefaultResultsFilter, getVariantAnalysisDefaultResultsSort, + isCanary, } from "../config"; export class VariantAnalysisView @@ -53,6 +54,13 @@ export class VariantAnalysisView panel.reveal(undefined, true); await this.waitForPanelLoaded(); + + await this.postMessage({ + t: "setVariantAnalysisUserSettings", + variantAnalysisUserSettings: { + shouldShowViewAutofixesButton: isCanary(), + }, + }); } public async updateView(variantAnalysis: VariantAnalysis): Promise { @@ -135,6 +143,12 @@ export class VariantAnalysisView case "openQueryText": await this.manager.openQueryText(this.variantAnalysisId); break; + case "viewAutofixes": + await this.manager.viewAutofixes( + this.variantAnalysisId, + msg.filterSort, + ); + break; case "copyRepositoryList": await this.manager.copyRepoListToClipboard( this.variantAnalysisId, diff --git a/extensions/ql-vscode/src/variant-analysis/view-autofixes.ts b/extensions/ql-vscode/src/variant-analysis/view-autofixes.ts new file mode 100644 index 00000000000..16bbac07bd8 --- /dev/null +++ b/extensions/ql-vscode/src/variant-analysis/view-autofixes.ts @@ -0,0 +1,912 @@ +import type { RepositoriesFilterSortStateWithIds } from "./shared/variant-analysis-filter-sort"; +import { + defaultFilterSortState, + filterAndSortRepositoriesWithResults, +} from "./shared/variant-analysis-filter-sort"; +import type { + VariantAnalysis, + VariantAnalysisRepositoryTask, +} from "./shared/variant-analysis"; +import type { Credentials } from "../common/authentication"; +import { extLogger } from "../common/logging/vscode"; +import type { App } from "../common/app"; +import type { CodeQLCliServer } from "../codeql-cli/cli"; +import { + pathExists, + ensureDir, + remove, + unlink, + readFile, + writeFile, + createWriteStream, +} from "fs-extra"; +import { + withProgress, + progressUpdate, + reportStreamProgress, +} from "../common/vscode/progress"; +import type { ProgressCallback } from "../common/vscode/progress"; +import { join, parse } from "path"; +import { pluralize } from "../common/word"; +import { readRepoTask } from "./repo-tasks-store"; +import { tmpDir } from "../tmp-dir"; +import { spawn } from "child_process"; +import type { execFileSync } from "child_process"; +import { tryOpenExternalFile } from "../common/vscode/external-files"; +import type { VariantAnalysisManager } from "./variant-analysis-manager"; +import type { VariantAnalysisResultsManager } from "./variant-analysis-results-manager"; +import { + getAutofixPath, + getAutofixModel, + downloadTimeout, + AUTOFIX_PATH, + AUTOFIX_MODEL, +} from "../config"; +import { asError, getErrorMessage } from "../common/helpers-pure"; +import { createTimeoutSignal } from "../common/fetch-stream"; +import { unzipToDirectoryConcurrently } from "../common/unzip-concurrently"; +import { reportUnzipProgress } from "../common/vscode/unzip-progress"; +import { getDirectoryNamesInsidePath } from "../common/files"; +import { Readable } from "stream"; + +// Limit to three repos when generating autofixes so not sending +// too many requests to autofix. Since we only need to validate +// a handle of autofixes for each query, this should be sufficient. +// Consider increasing this in the future if needed. +const MAX_NUM_REPOS: number = 3; +// Similarly, limit to three fixes per repo. +const MAX_NUM_FIXES: number = 3; + +/** + * Generates autofixes for the results of a variant analysis. + */ +export async function viewAutofixesForVariantAnalysisResults( + variantAnalysisManager: VariantAnalysisManager, + variantAnalysisResultsManager: VariantAnalysisResultsManager, + variantAnalysisId: number, + filterSort: RepositoriesFilterSortStateWithIds = defaultFilterSortState, + credentials: Credentials, + app: App, + cliServer: CodeQLCliServer, +): Promise { + await withProgress( + async (progress: ProgressCallback) => { + // Get the path to the local autofix installation. + const localAutofixPath = await findLocalAutofix(); + + // Get the variant analysis with the given id. + const variantAnalysis = + variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); + if (!variantAnalysis) { + throw new Error(`No variant analysis with id: ${variantAnalysisId}`); + } + + // Generate the query help and output it to the override directory. + await overrideQueryHelp(variantAnalysis, cliServer, localAutofixPath); + + // Get the full names (nwos) of the selected repositories. + const selectedRepoNames = getSelectedRepositoryNames( + variantAnalysis, + filterSort, + ); + + // Get storage paths for the autofix results. + const { + variantAnalysisIdStoragePath, + sourceRootsStoragePath, + autofixOutputStoragePath, + } = await getStoragePaths(variantAnalysisManager, variantAnalysisId); + + // Process the selected repositories: + // Get sarif + // Download source root + // Run autofix and output results + progress( + progressUpdate( + 1, + 2, + `Processing ${pluralize(selectedRepoNames.length, "repository", "repositories")}`, + ), + ); + const outputTextFiles = await processSelectedRepositories( + variantAnalysisResultsManager, + selectedRepoNames, + variantAnalysisIdStoragePath, + sourceRootsStoragePath, + autofixOutputStoragePath, + localAutofixPath, + credentials, + ); + + // Output results from all repos to a combined markdown file. + progress(progressUpdate(2, 2, `Finalizing autofix results`)); + const combinedOutputMarkdownFile = join( + autofixOutputStoragePath, + "autofix-output.md", + ); + await mergeFiles(outputTextFiles, combinedOutputMarkdownFile, false); + + // Open the combined markdown file. + await tryOpenExternalFile(app.commands, combinedOutputMarkdownFile); + }, + { + title: "Generating Autofixes", + cancellable: false, // not cancellable for now + }, + ); +} + +/** + * Finds the local autofix installation path from the `codeQL.autofix.path` setting. + * Throws an error if the path is not set or does not exist. + * @returns An object containing the local autofix path. + * @throws Error if the `codeQL.autofix.path` setting is not set or the path does not exist. + */ +async function findLocalAutofix(): Promise { + const localAutofixPath = getAutofixPath(); + if (!localAutofixPath) { + throw new Error( + `Path to local autofix installation not found. Make sure ${AUTOFIX_PATH.qualifiedName} is set correctly. Internal GitHub access required.`, + ); + } + if (!(await pathExists(localAutofixPath))) { + throw new Error(`Local autofix path ${localAutofixPath} does not exist.`); + } + return localAutofixPath; +} + +/** + * Overrides the query help from a given variant analysis + * at a location within the `localAutofixPath` directory . + */ +async function overrideQueryHelp( + variantAnalysis: VariantAnalysis, + cliServer: CodeQLCliServer, + localAutofixPath: string, +): Promise { + // Get path to the query used by the variant analysis. + const queryFilePath = variantAnalysis.query.filePath; + if (!(await pathExists(queryFilePath))) { + throw new Error(`Query file used by variant analysis not found.`); + } + const parsedQueryFilePath = parse(queryFilePath); + const queryFilePathNoExt = join( + parsedQueryFilePath.dir, + parsedQueryFilePath.name, + ); + + // Get the path to the query help, which may be either a `.qhelp` or a `.md` file. + // Note: we assume that the name of the query file is the same as the name of the query help file. + const queryHelpFilePathQhelp = `${queryFilePathNoExt}.qhelp`; + const queryHelpFilePathMarkdown = `${queryFilePathNoExt}.md`; + + // Set `queryHelpFilePath` to the existing extension type. + let queryHelpFilePath: string; + if (await pathExists(queryHelpFilePathQhelp)) { + queryHelpFilePath = queryHelpFilePathQhelp; + } else if (await pathExists(queryHelpFilePathMarkdown)) { + queryHelpFilePath = queryHelpFilePathMarkdown; + } else { + throw new Error( + `Could not find query help file at either ${queryHelpFilePathQhelp} or ${queryHelpFilePathMarkdown}. Check that the query help file exists and is named correctly.`, + ); + } + + // Get the query metadata. + let metadata; + try { + metadata = await cliServer.resolveMetadata(queryFilePath); + } catch (e) { + throw new Error( + `Could not resolve query metadata for ${queryFilePath}. Reason: ${getErrorMessage(e)}`, + ); + } + // Get the query ID (used for the overridden query help's filename). + const queryId = metadata.id; + if (!queryId) { + throw new Error(`Query metadata for ${queryFilePath} is missing an ID.`); + } + // Replace `/` with `-` for use with the overridden query help's filename. + // Use `replaceAll` since some query IDs have multiple slashes. + const queryIdWithDash = queryId.replaceAll("/", "-"); + + // Get the path to the output directory for overriding the query help. + // Note: the path to this directory may change in the future. + const queryHelpOverrideDirectory = join( + localAutofixPath, + "prompt-templates", + "qhelps", + `${queryIdWithDash}.md`, + ); + + await cliServer.generateQueryHelp( + queryHelpFilePath, + queryHelpOverrideDirectory, + ); +} + +/** + * Gets the full names (owner/repo) of the selected + * repositories from the given variant analysis while + * limiting the number of repositories to `MAX_NUM_REPOS`. + */ +function getSelectedRepositoryNames( + variantAnalysis: VariantAnalysis, + filterSort: RepositoriesFilterSortStateWithIds, +): string[] { + // Get the repositories that were selected by the user. + const filteredRepositories = filterAndSortRepositoriesWithResults( + variantAnalysis.scannedRepos, + filterSort, + ); + + // Get the full names (owner/repo = nwo) of the selected repos. + let fullNames = filteredRepositories + ?.filter((a) => a.resultCount && a.resultCount > 0) + .map((a) => a.repository.fullName); + if (!fullNames || fullNames.length === 0) { + throw new Error("No repositories with results found."); + } + + // Limit to MAX_NUM_REPOS by slicing the array. + if (fullNames.length > MAX_NUM_REPOS) { + fullNames = fullNames.slice(0, MAX_NUM_REPOS); + void extLogger.showWarningMessage( + `Only the first ${MAX_NUM_REPOS} repos (${fullNames.join(", ")}) will be included in the Autofix results.`, + ); + } + + return fullNames; +} + +/** + * Gets the storage paths needed for the autofix results. + */ +async function getStoragePaths( + variantAnalysisManager: VariantAnalysisManager, + variantAnalysisId: number, +): Promise<{ + variantAnalysisIdStoragePath: string; + sourceRootsStoragePath: string; + autofixOutputStoragePath: string; +}> { + // Confirm storage path for the variant analysis ID exists. + const variantAnalysisIdStoragePath = + variantAnalysisManager.getVariantAnalysisStorageLocation(variantAnalysisId); + if (!(await pathExists(variantAnalysisIdStoragePath))) { + throw new Error( + `Variant analysis storage location does not exist: ${variantAnalysisIdStoragePath}`, + ); + } + + // Storage path for all autofix info. + const autofixStoragePath = join(variantAnalysisIdStoragePath, "autofix"); + + // Storage path for the source roots used with autofix. + const sourceRootsStoragePath = join(autofixStoragePath, "source-roots"); + await ensureDir(sourceRootsStoragePath); + + // Storage path for the autofix output. + let autofixOutputStoragePath = join(autofixStoragePath, "output"); + // If the path already exists, assume that it's a previous run + // and append "-n" to the end of the path where n is the next available number. + if (await pathExists(autofixOutputStoragePath)) { + let i = 1; + while (await pathExists(autofixOutputStoragePath + i.toString())) { + i++; + } + autofixOutputStoragePath = autofixOutputStoragePath += i.toString(); + } + await ensureDir(autofixOutputStoragePath); + + return { + variantAnalysisIdStoragePath, + sourceRootsStoragePath, + autofixOutputStoragePath, + }; +} + +/** + * Processes the selected repositories for autofix generation. + */ +async function processSelectedRepositories( + variantAnalysisResultsManager: VariantAnalysisResultsManager, + selectedRepoNames: string[], + variantAnalysisIdStoragePath: string, + sourceRootsStoragePath: string, + autofixOutputStoragePath: string, + localAutofixPath: string, + credentials: Credentials, +): Promise { + const outputTextFiles: string[] = []; + await Promise.all( + selectedRepoNames.map(async (nwo) => + withProgress( + async (progressForRepo: ProgressCallback) => { + // Get the sarif file. + progressForRepo(progressUpdate(1, 3, `Getting sarif...`)); + const sarifFile = await getRepoSarifFile( + variantAnalysisResultsManager, + variantAnalysisIdStoragePath, + nwo, + ); + + // Read the contents of the variant analysis' `repo_task.json` file, + // and confirm that the `databaseCommitSha` and `resultCount` exist. + const repoTask: VariantAnalysisRepositoryTask = await readRepoTask( + variantAnalysisResultsManager.getRepoStorageDirectory( + variantAnalysisIdStoragePath, + nwo, + ), + ); + if (!repoTask.databaseCommitSha) { + throw new Error(`Missing database commit SHA for ${nwo}`); + } + if (!repoTask.resultCount) { + throw new Error(`Missing variant analysis result count for ${nwo}`); + } + + // Download the source root. + // Using `0` as the progress step to force a dynamic vs static progress bar. + progressForRepo(progressUpdate(0, 3, `Fetching source root...`)); + const srcRootPath = await downloadPublicCommitSource( + nwo, + repoTask.databaseCommitSha, + sourceRootsStoragePath, + credentials, + progressForRepo, + ); + + // Run autofix. + progressForRepo(progressUpdate(2, 3, `Running autofix...`)); + await runAutofixForRepository( + nwo, + sarifFile, + srcRootPath, + localAutofixPath, + autofixOutputStoragePath, + repoTask.resultCount, + outputTextFiles, + ); + }, + { + title: `${nwo}`, + cancellable: false, + }, + ), + ), + ); + + return outputTextFiles; +} + +/** + * Gets the path to a SARIF file for a given `nwo`. + */ +async function getRepoSarifFile( + variantAnalysisResultsManager: VariantAnalysisResultsManager, + variantAnalysisIdStoragePath: string, + nwo: string, +): Promise { + if ( + !(await variantAnalysisResultsManager.isVariantAnalysisRepoDownloaded( + variantAnalysisIdStoragePath, + nwo, + )) + ) { + throw new Error(`Variant analysis results not downloaded for ${nwo}`); + } + const sarifFile = + variantAnalysisResultsManager.getRepoResultsSarifStoragePath( + variantAnalysisIdStoragePath, + nwo, + ); + if (!(await pathExists(sarifFile))) { + throw new Error(`SARIF file not found for ${nwo}`); + } + return sarifFile; +} + +/** + * Downloads the source code of a public commit from a GitHub repository. + */ +async function downloadPublicCommitSource( + nwo: string, + sha: string, + outputPath: string, + credentials: Credentials, + progressCallback: ProgressCallback, +): Promise { + const [owner, repo] = nwo.split("/"); + if (!owner || !repo) { + throw new Error(`Invalid repository name: ${nwo}`); + } + + // Create output directory if it doesn't exist + await ensureDir(outputPath); + + // Define the final checkout directory + const checkoutDir = join( + outputPath, + `${owner}-${repo}-${sha.substring(0, 7)}`, + ); + + // Check if directory already exists to avoid re-downloading + if (await pathExists(checkoutDir)) { + const dirNames = await getDirectoryNamesInsidePath(checkoutDir); + if (dirNames.length === 1) { + // The path to the source code should be a single directory inside `checkoutDir`. + const sourceRootDir = join(checkoutDir, dirNames[0]); + void extLogger.log( + `Source for ${nwo} at ${sha} already exists at ${sourceRootDir}.`, + ); + return sourceRootDir; + } else { + // Remove `checkoutDir` to allow a re-download if the directory structure is unexpected. + void extLogger.log( + `Unexpected directory structure. Removing ${checkoutDir}`, + ); + await remove(checkoutDir); + } + } + + void extLogger.log(`Fetching source of repository ${nwo} at ${sha}...`); + + try { + const octokit = await credentials.getOctokit(); + + // Get the zipball URL + const { url } = await octokit.rest.repos.downloadZipballArchive({ + owner, + repo, + ref: sha, + }); + + // Create a temporary directory for downloading + const archivePath = join( + tmpDir.name, + `source-${owner}-${repo}-${Date.now()}.zip`, + ); + + // Set timeout + const { + signal, + onData, + dispose: disposeTimeout, + } = createTimeoutSignal(downloadTimeout()); + + // Fetch the url + let response: Response; + try { + response = await fetch(url, { + headers: { + Accept: "application/zip", + }, + signal, + }); + } catch (e) { + disposeTimeout(); + + if (e instanceof DOMException && e.name === "AbortError") { + const thrownError = new Error("The request timed out."); + thrownError.stack = e.stack; + throw thrownError; + } + throw new Error( + `Error fetching source root. Reason: ${getErrorMessage(e)}`, + ); + } + + // Download the source root from the response body + const body = response.body; + if (!body) { + throw new Error("No response body found"); + } + + const archiveFileStream = createWriteStream(archivePath); + + const contentLength = response.headers.get("content-length"); + const totalNumBytes = contentLength + ? parseInt(contentLength, 10) + : undefined; + + const reportProgress = reportStreamProgress( + "Downloading source root...", + totalNumBytes, + progressCallback, + ); + + try { + const readable = Readable.fromWeb(body); + readable.on("data", (chunk) => { + onData(); + reportProgress(chunk?.length ?? 0); + }); + await new Promise((resolve, reject) => { + readable + .pipe(archiveFileStream) + .on("error", (err) => { + reject(err); + }) + .on("finish", () => resolve(undefined)); + }); + + await new Promise((resolve, reject) => { + archiveFileStream.close((err) => { + if (err) { + reject(err); + } + resolve(undefined); + }); + }); + } catch (e) { + // Close and remove the file if an error occurs + archiveFileStream.close(() => { + void remove(archivePath); + }); + + if (e instanceof DOMException && e.name === "AbortError") { + const thrownError = new Error("The download timed out."); + thrownError.stack = e.stack; + throw thrownError; + } + + throw new Error( + `Error downloading source root. Reason: ${getErrorMessage(e)}`, + ); + } finally { + disposeTimeout(); + } + + void extLogger.log(`Download complete, extracting source...`); + + // Extract the downloaded zip file + await unzipToDirectoryConcurrently( + archivePath, + checkoutDir, + reportUnzipProgress(`Unzipping source root...`, progressCallback), + ); + await remove(archivePath); + + // Since `unzipToDirectoryConcurrently` extracts to a directory within + // `checkoutDir`, we need to return the path to that extracted directory. + const dirNames = await getDirectoryNamesInsidePath(checkoutDir); + if (dirNames.length === 1) { + return join(checkoutDir, dirNames[0]); + } else { + throw new Error( + `Expected exactly one unzipped source directory for ${nwo}, but found ${dirNames.length}.`, + ); + } + } catch (error) { + await remove(checkoutDir); + throw new Error( + `Failed to download ${nwo} at ${sha}:. Reason: ${getErrorMessage(error)}`, + ); + } +} + +/** + * Runs autofix for a given repository (nwo). + */ +async function runAutofixForRepository( + nwo: string, + sarifFile: string, + srcRootPath: string, + localAutofixPath: string, + autofixOutputStoragePath: string, + resultCount: number, + outputTextFiles: string[], +): Promise { + // Get storage paths for the autofix results for this repository. + const { + repoAutofixOutputStoragePath, + outputTextFilePath, + transcriptFilePath, + fixDescriptionFilePath, + } = await getRepoStoragePaths(autofixOutputStoragePath, nwo); + + // Get autofix binary. + // Switch to Go binary in the future and have user pass full path + // in an environment variable instead of hardcoding part here. + const cocofixBin = join(process.cwd(), localAutofixPath, "bin", "cocofix.js"); + + // Limit number of fixes generated. + const limitFixesBoolean: boolean = resultCount > MAX_NUM_FIXES; + if (limitFixesBoolean) { + void extLogger.log( + `Only generating autofixes for the first ${MAX_NUM_FIXES} alerts for ${nwo}.`, + ); + + // Run autofix in a loop for the first MAX_NUM_FIXES alerts. + // Not an ideal solution, but avoids modifying the input SARIF file. + const tempOutputTextFiles: string[] = []; + const fixDescriptionFiles: string[] = []; + const transcriptFiles: string[] = []; + for (let i = 0; i < MAX_NUM_FIXES; i++) { + const tempOutputTextFilePath = appendSuffixToFilePath( + outputTextFilePath, + i.toString(), + ); + const tempFixDescriptionFilePath = appendSuffixToFilePath( + fixDescriptionFilePath, + i.toString(), + ); + const tempTranscriptFilePath = appendSuffixToFilePath( + transcriptFilePath, + i.toString(), + ); + + tempOutputTextFiles.push(tempOutputTextFilePath); + fixDescriptionFiles.push(tempFixDescriptionFilePath); + transcriptFiles.push(tempTranscriptFilePath); + + await runAutofixOnResults( + cocofixBin, + sarifFile, + srcRootPath, + tempOutputTextFilePath, + tempFixDescriptionFilePath, + tempTranscriptFilePath, + repoAutofixOutputStoragePath, + i, + ); + } + + // Merge the output files together. + // Caveat that autofix will call each alert "alert 0", which will look a bit odd in the merged output file. + await mergeFiles(tempOutputTextFiles, outputTextFilePath); + await mergeFiles(fixDescriptionFiles, fixDescriptionFilePath); + await mergeFiles(transcriptFiles, transcriptFilePath); + } else { + // Run autofix once for all alerts. + await runAutofixOnResults( + cocofixBin, + sarifFile, + srcRootPath, + outputTextFilePath, + fixDescriptionFilePath, + transcriptFilePath, + repoAutofixOutputStoragePath, + ); + } + + // Format the output text file with markdown. + await formatWithMarkdown(outputTextFilePath, `${nwo}`); + + // Save output text files from each repo to later merge + // into a single markdown file containing all results. + outputTextFiles.push(outputTextFilePath); +} + +/** + * Gets the storage paths for the autofix results for a given repository. + */ +async function getRepoStoragePaths( + autofixOutputStoragePath: string, + nwo: string, +) { + // Create output directories for repo's autofix results. + const repoAutofixOutputStoragePath = join( + autofixOutputStoragePath, + nwo.replaceAll("/", "-"), + ); + await ensureDir(repoAutofixOutputStoragePath); + return { + repoAutofixOutputStoragePath, + outputTextFilePath: join(repoAutofixOutputStoragePath, "output.txt"), + transcriptFilePath: join(repoAutofixOutputStoragePath, "transcript.md"), + fixDescriptionFilePath: join( + repoAutofixOutputStoragePath, + "fix-description.md", + ), + }; +} + +/** + * Runs autofix on the results in the given SARIF file. + */ +async function runAutofixOnResults( + cocofixBin: string, + sarifFile: string, + srcRootPath: string, + outputTextFilePath: string, + fixDescriptionFilePath: string, + transcriptFilePath: string, + workDir: string, + alertNumber?: number, // Optional parameter for specific alert +): Promise { + // Get autofix model from user settings. + const autofixModel = getAutofixModel(); + if (!autofixModel) { + throw new Error( + `Autofix model not found. Make sure ${AUTOFIX_MODEL.qualifiedName} is set correctly.`, + ); + } + // Set up args for autofix command. + const autofixArgs = [ + "--sarif", + sarifFile, + "--source-root", + srcRootPath, + "--model", + autofixModel, + "--dev", + "--no-cache", + "--format", + "text", + "--diff-style", + "diff", // could do "text" instead if want line of "=" between fixes + "--output", + outputTextFilePath, + "--fix-description", + fixDescriptionFilePath, + "--transcript", + transcriptFilePath, + ]; + + // Add alert number argument if provided + if (alertNumber !== undefined) { + autofixArgs.push("--only-alert-number", alertNumber.toString()); + } + + await execAutofix( + cocofixBin, + autofixArgs, + { + cwd: workDir, + env: { + CAPI_DEV_KEY: process.env.CAPI_DEV_KEY, + PATH: process.env.PATH, + }, + }, + true, + ); +} + +/** + * Executes the autofix command. + */ +function execAutofix( + bin: string, + args: string[], + options: Parameters[2], + showCommand?: boolean, +): Promise { + return new Promise((resolve, reject) => { + try { + const cwd = options?.cwd || process.cwd(); + if (showCommand) { + void extLogger.log(`Spawning '${bin} ${args.join(" ")}' in ${cwd}`); + } + + let stdoutBuffer = ""; + let stderrBuffer = ""; + + const p = spawn(bin, args, { + stdio: ["ignore", "pipe", "pipe"], + ...options, + }); + + // Listen for stdout + p.stdout?.on("data", (data) => { + stdoutBuffer += data.toString(); + }); + + // Listen for stderr + p.stderr?.on("data", (data) => { + stderrBuffer += data.toString(); + }); + + // Listen for errors + p.on("error", reject); + + // Listen for process exit + p.on("exit", (code) => { + // Log collected output + if (stdoutBuffer.trim()) { + void extLogger.log(`Autofix stdout:\n${stdoutBuffer.trim()}`); + } + + if (stderrBuffer.trim()) { + void extLogger.log(`Autofix stderr:\n${stderrBuffer.trim()}`); + } + + if (code === 0) { + resolve(); + } else { + reject(new Error(`Autofix process exited with code ${code}.`)); + } + }); + } catch (e) { + reject(asError(e)); + } + }); +} + +/** + * Creates a new file path by appending the given suffix. + * @param filePath The original file path. + * @param suffix The suffix to append to the file name (before the extension). + * @returns The new file path with the suffix appended. + */ +function appendSuffixToFilePath(filePath: string, suffix: string): string { + const { dir, name, ext } = parse(filePath); + return join(dir, `${name}-${suffix}${ext}`); +} + +/** + * Merges the given `inputFiles` into a single `outputFile`. + * @param inputFiles - The list of input files to merge. + * @param outputFile - The output file path. + * @param deleteOriginalFiles - Whether to delete the original input files after merging. + */ +async function mergeFiles( + inputFiles: string[], + outputFile: string, + deleteOriginalFiles: boolean = true, +): Promise { + try { + // Check if any input files do not exist and return if so. + const pathChecks = await Promise.all( + inputFiles.map(async (path) => ({ + exists: await pathExists(path), + })), + ); + const anyPathMissing = pathChecks.some((check) => !check.exists); + if (inputFiles.length === 0 || anyPathMissing) { + return; + } + + // Merge the files + const contents = await Promise.all( + inputFiles.map((file) => readFile(file, "utf8")), + ); + + // Write merged content + await writeFile(outputFile, contents.join("\n")); + + // Delete original files + if (deleteOriginalFiles) { + await Promise.all(inputFiles.map((file) => unlink(file))); + } + } catch (error) { + throw new Error(`Error merging files. Reason: ${getErrorMessage(error)}`); + } +} + +/** + * Formats the given input file with the specified header. + * @param inputFile The path to the input file to format. + * @param header The header to include in the formatted output. + */ +async function formatWithMarkdown( + inputFile: string, + header: string, +): Promise { + try { + // Check if the input file exists + const exists = await pathExists(inputFile); + if (!exists) { + return; + } + + // Read the input file content + const content = await readFile(inputFile, "utf8"); + + const frontFormatting: string = + "
Fix suggestion details\n\n```diff\n"; + + const backFormatting: string = + "```\n\n
\n\n ### Notes\n - notes placeholder\n\n"; + + // Format the content with Markdown + // Replace ``` in the content with \``` to avoid breaking the Markdown code block + const formattedContent = `## ${header}\n\n${frontFormatting}${content.replaceAll("```", "\\```")}${backFormatting}`; + + // Write the formatted content back to the file + await writeFile(inputFile, formattedContent); + } catch (error) { + throw new Error(`Error formatting file. Reason: ${getErrorMessage(error)}`); + } +} diff --git a/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css new file mode 100644 index 00000000000..70a8e8a1577 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css @@ -0,0 +1,56 @@ +/* Styles have been copied from https://vscode-elements.github.io/elements-lite/components/action-button/configurator/ */ +.actionButton { + align-items: center; + background-color: transparent; + border-color: transparent; + border-style: solid; + border-width: 1px; + border-radius: 5px; + color: var(--vscode-foreground); + display: inline-flex; + cursor: pointer; + padding: 0; + user-select: none; +} + +.actionButton:disabled { + color: var(--vscode-disabledForeground); + cursor: default; + pointer-events: none; +} + +.actionButton :global(.codicon), +.actionButton svg { + color: var(--vscode-icon-foreground); + display: block; + padding: 2px; +} + +.actionButton svg { + box-sizing: content-box; + height: 16px; + width: 16px; +} + +.actionButton:disabled :global(.codicon), +.actionButton:disabled svg { + color: var(--vscode-disabledForeground); +} + +.actionButton:hover { + background-color: var(--vscode-toolbar-hoverBackground); + outline: 1px dotted var(--vscode-contrastActiveBorder, transparent); + outline-offset: -1px; +} + +.actionButton:active { + background-color: var(--vscode-toolbar-activeBackground); +} + +.actionButton:focus { + outline: none; +} + +.actionButton:focus-visible { + border-color: var(--vscode-focusBorder); +} diff --git a/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css.d.ts b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css.d.ts new file mode 100644 index 00000000000..e29a2ad9062 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.module.css.d.ts @@ -0,0 +1,2 @@ +declare const styles: { [className: string]: string }; +export default styles; diff --git a/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.tsx b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.tsx new file mode 100644 index 00000000000..f673fd56f96 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/ActionButton/ActionButton.tsx @@ -0,0 +1,9 @@ +import styles from "./ActionButton.module.css"; + +// This is needed because vscode-elements/elements does not implement +// the same styles for icon buttons as vscode/webview-ui-toolkit +export const ActionButton = (props: React.ComponentProps<"button">) => ( + +); diff --git a/extensions/ql-vscode/src/view/common/Badge.tsx b/extensions/ql-vscode/src/view/common/Badge.tsx new file mode 100644 index 00000000000..cb9826bcab8 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/Badge.tsx @@ -0,0 +1,8 @@ +import { VscodeBadge } from "@vscode-elements/react-elements"; + +// This applies the counter variant by default so the border-radius attribute is set +export const Badge = (props: React.ComponentProps) => ( + + {props.children} + +); diff --git a/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx b/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx index 277013084f1..cc232060f0a 100644 --- a/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx +++ b/extensions/ql-vscode/src/view/common/CodePaths/CodeFlowsDropdown.tsx @@ -1,6 +1,9 @@ import type { ChangeEvent, SetStateAction } from "react"; import { useCallback } from "react"; -import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"; +import { + VscodeOption, + VscodeSingleSelect, +} from "@vscode-elements/react-elements"; import type { CodeFlow } from "../../../variant-analysis/shared/analysis-result"; @@ -35,12 +38,12 @@ export const CodeFlowsDropdown = ({ .toString(); return ( - + {codeFlows.map((codeFlow, index) => ( - + {getCodeFlowName(codeFlow)} - + ))} - + ); }; diff --git a/extensions/ql-vscode/src/view/common/Link.tsx b/extensions/ql-vscode/src/view/common/Link.tsx index 9fa73906433..a0e37b92bcd 100644 --- a/extensions/ql-vscode/src/view/common/Link.tsx +++ b/extensions/ql-vscode/src/view/common/Link.tsx @@ -3,12 +3,12 @@ import { styled } from "styled-components"; export const Link = styled.a` background: transparent; box-sizing: border-box; - color: var(--link-foreground); + color: var(--vscode-textLink-foreground); cursor: pointer; fill: currentcolor; - font-family: var(--font-family); - font-size: var(--type-ramp-base-font-size); - line-height: var(--type-ramp-base-line-height); + font-family: var(--vscode-font-family); + font-size: var(--vscode-font-size); + line-height: normal; outline: none; &:hover { diff --git a/extensions/ql-vscode/src/view/common/SearchBox.tsx b/extensions/ql-vscode/src/view/common/SearchBox.tsx index 464d303fa7e..7837c143e76 100644 --- a/extensions/ql-vscode/src/view/common/SearchBox.tsx +++ b/extensions/ql-vscode/src/view/common/SearchBox.tsx @@ -1,12 +1,16 @@ import { useCallback } from "react"; import { styled } from "styled-components"; -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; +import { VscodeTextfield } from "@vscode-elements/react-elements"; import { Codicon } from "./icon"; -const TextField = styled(VSCodeTextField)` +const TextField = styled(VscodeTextfield)` width: 100%; `; +const SearchIcon = styled(Codicon)` + margin: 0 8px; +`; + type Props = { value: string; placeholder: string; @@ -37,7 +41,7 @@ export const SearchBox = ({ onInput={handleInput} className={className} > - + ); }; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx index 32240fac67e..f59d5f96093 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -14,15 +14,15 @@ import { useRole, } from "@floating-ui/react"; import { css, styled } from "styled-components"; -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; import type { Option } from "./options"; import { findMatchingOptions } from "./options"; import { SuggestBoxItem } from "./SuggestBoxItem"; import { LabelText } from "./LabelText"; import type { Diagnostic } from "./diagnostics"; import { useOpenKey } from "./useOpenKey"; +import { VscodeTextfield } from "@vscode-elements/react-elements"; -const Input = styled(VSCodeTextField)<{ $error: boolean }>` +const Input = styled(VscodeTextfield)<{ $error: boolean }>` width: 100%; font-family: var(--vscode-editor-font-family); @@ -96,7 +96,7 @@ export type SuggestBoxProps< /** * Can be used to render a different component for the input. This is used - * in testing to use default HTML components rather than the VSCodeTextField + * in testing to use default HTML components rather than the VscodeTextfield * for easier testing. * @param props The props returned by `getReferenceProps` of {@link useInteractions} */ @@ -200,8 +200,6 @@ export const SuggestBox = < }, [disabled]); return ( - // Disabled because the div is used to stop click propagation, it's not a button - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
{renderInputComponent( getReferenceProps({ diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/useOpenKey.ts b/extensions/ql-vscode/src/view/common/SuggestBox/useOpenKey.ts index e0d158c8930..5e646286cd1 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/useOpenKey.ts +++ b/extensions/ql-vscode/src/view/common/SuggestBox/useOpenKey.ts @@ -16,24 +16,22 @@ export const useOpenKey = ( ): ElementProps => { const { open, onOpenChange } = context; - const openOnOpenKey = useEffectEvent( - (event: KeyboardEvent | KeyboardEvent) => { - if (open) { - return; - } + const openOnOpenKey = useEffectEvent((event: KeyboardEvent) => { + if (open) { + return; + } - if ( - event.key === " " && - event.ctrlKey && - !event.altKey && - !event.metaKey && - !event.shiftKey - ) { - event.preventDefault(); - onOpenChange(true, isReactEvent(event) ? event.nativeEvent : event); - } - }, - ); + if ( + event.key === " " && + event.ctrlKey && + !event.altKey && + !event.metaKey && + !event.shiftKey + ) { + event.preventDefault(); + onOpenChange(true, isReactEvent(event) ? event.nativeEvent : event); + } + }); return useMemo((): ElementProps => { return { diff --git a/extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx b/extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx index d072310ea9c..c9fac0a09dc 100644 --- a/extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx +++ b/extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx @@ -18,7 +18,7 @@ import type { } from "../../log-insights/performance-comparison"; import { formatDecimal } from "../../common/number"; import { styled } from "styled-components"; -import { Codicon, ViewTitle, WarningBox } from "../common"; +import { Codicon, ViewTitle } from "../common"; import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter"; import { Renaming, RenamingInput } from "./RenamingInput"; @@ -35,6 +35,8 @@ function isPresent(x: Optional): x is T { } interface PredicateInfo { + name: string; + raHash: string; tuples: number; evaluationCount: number; iterationCount: number; @@ -43,23 +45,37 @@ interface PredicateInfo { } class ComparisonDataset { + /** + * Predicates indexed by a key consisting of the name and its pipeline hash. + * Unlike the RA hash, the pipeline hash only depends on the predicate's own pipeline. + */ + public keyToIndex = new Map(); + public raToIndex = new Map(); public nameToIndex = new Map(); public cacheHitIndices: Set; public sentinelEmptyIndices: Set; - constructor(public data: PerformanceComparisonDataFromLog) { - const { names } = data; - const { nameToIndex } = this; + constructor(private data: PerformanceComparisonDataFromLog) { + const { names, raHashes, pipelineSummaryList } = data; + const { keyToIndex, raToIndex, nameToIndex } = this; for (let i = 0; i < names.length; i++) { - nameToIndex.set(names[i], i); + const name = names[i]; + const pipelineHash = getPipelineSummaryHash(pipelineSummaryList[i]); + keyToIndex.set(`${name}@${pipelineHash}`, i); + nameToIndex.set(name, i); + raToIndex.set(raHashes[i], i); } this.cacheHitIndices = new Set(data.cacheHitIndices); this.sentinelEmptyIndices = new Set(data.sentinelEmptyIndices); } - getTupleCountInfo(name: string): Optional { - const { data, nameToIndex, cacheHitIndices, sentinelEmptyIndices } = this; - const index = nameToIndex.get(name); + keys() { + return Array.from(this.keyToIndex.keys()); + } + + getTupleCountInfo(key: string): Optional { + const { data, keyToIndex, cacheHitIndices, sentinelEmptyIndices } = this; + const index = keyToIndex.get(key); if (index == null) { return AbsentReason.NotSeen; } @@ -72,6 +88,8 @@ class ComparisonDataset { } } return { + name: data.names[index], + raHash: data.raHashes[index], evaluationCount: data.evaluationCounts[index], iterationCount: data.iterationCounts[index], timeCost: data.timeCosts[index], @@ -79,9 +97,78 @@ class ComparisonDataset { pipelines: data.pipelineSummaryList[index], }; } + + /** + * Returns the RA hashes of all predicates that were evaluated in this data set, but not seen in `other`, + * because in `other` the dependency upon these predicates was cut off by a cache hit. + * + * For example, suppose predicate `A` depends on `B`, which depends on `C`, and the + * predicates were evaluated in the first log but not the second: + * ``` + * first eval. log second eval. log + * predicate A seen evaluation seen cache hit + * | + * V + * predicate B seen evaluation not seen + * | + * V + * predicate C seen evaluation not seen + * ``` + * + * To ensure a meaningful comparison, we want to omit `predicate A` from the comparison view because of the cache hit. + * + * But predicates B and C did not have a recorded cache hit in the second log, because they were never scheduled for evaluation. + * Given the dependency graph, the most likely explanation is that they would have been evaluated if `A` had not been a cache hit. + * We therefore say that B and C are "shadowed" by the cache hit on A. + * + * The dependency graph is only visible in the first evaluation log, because `B` and `C` do not exist in the second log. + * So to compute this, we use the dependency graph from one log together with the set of cache hits in the other log. + */ + getPredicatesShadowedByCacheHit(other: ComparisonDataset) { + const { + data: { dependencyLists, raHashes, names }, + raToIndex, + } = this; + const cacheHits = new Set(); + + function visit(index: number, raHash: string) { + if (cacheHits.has(raHash)) { + return; + } + cacheHits.add(raHash); + const dependencies = dependencyLists[index]; + for (const dep of dependencies) { + const name = names[dep]; + if (!other.nameToIndex.has(name)) { + visit(dep, raHashes[dep]); + } + } + } + + for (const otherCacheHit of other.cacheHitIndices) { + { + // Look up by RA hash + const raHash = other.data.raHashes[otherCacheHit]; + const ownIndex = raToIndex.get(raHash); + if (ownIndex != null) { + visit(ownIndex, raHash); + } + } + { + // Look up by name + const name = other.data.names[otherCacheHit]; + const ownIndex = this.nameToIndex.get(name); + if (ownIndex != null) { + visit(ownIndex, raHashes[ownIndex]); + } + } + } + + return cacheHits; + } } -function renderOptionalValue(x: Optional, unit?: string) { +function renderOptionalValue(x: Optional, unit: string | undefined) { switch (x) { case AbsentReason.NotSeen: return n/a; @@ -336,6 +423,7 @@ function HighLevelStats(props: HighLevelStatsProps) { } interface Row { + key: string; name: string; before: Optional; after: Optional; @@ -472,7 +560,7 @@ function ComparePerformanceWithData(props: { const comparison = data?.comparison; - const [hideCacheHits, setHideCacheHits] = useState(false); + const [hideCacheHits, setHideCacheHits] = useState(true); const [sortOrder, setSortOrder] = useState<"delta" | "absDelta">("absDelta"); @@ -480,19 +568,27 @@ function ComparePerformanceWithData(props: { const [isPerEvaluation, setPerEvaluation] = useState(false); - const nameSet = useMemo( - () => union(from.data.names, to.data.names), - [from, to], - ); + const keySet = useMemo(() => union(from.keys(), to.keys()), [from, to]); const hasCacheHitMismatch = useRef(false); + const shadowedCacheHitsFrom = useMemo( + () => + hideCacheHits ? from.getPredicatesShadowedByCacheHit(to) : new Set(), + [from, to, hideCacheHits], + ); + const shadowedCacheHitsTo = useMemo( + () => + hideCacheHits ? to.getPredicatesShadowedByCacheHit(from) : new Set(), + [from, to, hideCacheHits], + ); + const rows: Row[] = useMemo(() => { hasCacheHitMismatch.current = false; - return Array.from(nameSet) - .map((name) => { - const before = from.getTupleCountInfo(name); - const after = to.getTupleCountInfo(name); + return Array.from(keySet) + .map((key) => { + const before = from.getTupleCountInfo(key); + const after = to.getTupleCountInfo(key); const beforeValue = metricGetOptional(metric, before, isPerEvaluation); const afterValue = metricGetOptional(metric, after, isPerEvaluation); if (beforeValue === afterValue) { @@ -507,14 +603,39 @@ function ComparePerformanceWithData(props: { return undefined!; } } + if ( + (isPresent(before) && + !isPresent(after) && + shadowedCacheHitsFrom.has(before.raHash)) || + (isPresent(after) && + !isPresent(before) && + shadowedCacheHitsTo.has(after.raHash)) + ) { + return undefined!; + } const diff = (isPresent(afterValue) ? afterValue : 0) - (isPresent(beforeValue) ? beforeValue : 0); - return { name, before, after, diff } satisfies Row; + const name = isPresent(before) + ? before.name + : isPresent(after) + ? after.name + : key; + return { key, name, before, after, diff } satisfies Row; }) .filter((x) => !!x) .sort(getSortOrder(sortOrder)); - }, [nameSet, from, to, metric, hideCacheHits, sortOrder, isPerEvaluation]); + }, [ + keySet, + from, + to, + metric, + hideCacheHits, + sortOrder, + isPerEvaluation, + shadowedCacheHitsFrom, + shadowedCacheHitsTo, + ]); const { totalBefore, totalAfter, totalDiff } = useMemo(() => { let totalBefore = 0; @@ -575,23 +696,14 @@ function ComparePerformanceWithData(props: { <> Performance comparison {comparison && hasCacheHitMismatch.current && ( - - Inconsistent cache hits -
- Some predicates had a cache hit on one side but not the other. For - more accurate results, try running the{" "} - CodeQL: Clear Cache command before each query. -
-
- -
+ )} Compare{" "} @@ -712,8 +824,8 @@ function PredicateRowGroup(props: PredicateRowGroupProps) { - {comparison && renderOptionalValue(rowGroup.before)} - {renderOptionalValue(rowGroup.after)} + {comparison && renderOptionalValue(rowGroup.before, metric.unit)} + {renderOptionalValue(rowGroup.after, metric.unit)} {comparison && renderDelta(rowGroup.diff, metric.unit)} {renderedName} ({rowGroup.rows.length} predicates) @@ -860,3 +972,14 @@ function collatePipelines( function samePipeline(a: string[], b: string[]) { return a.length === b.length && a.every((x, i) => x === b[i]); } + +function getPipelineSummaryHash(pipelines: Record) { + // Note: we can't import "crypto" here because it is not available in the browser, + // so we just concatenate the hashes of the individual pipelines. + const keys = Object.keys(pipelines).sort(); + let result = ""; + for (const key of keys) { + result += `${pipelines[key].hash};`; + } + return result; +} diff --git a/extensions/ql-vscode/src/view/compare-performance/RenamingInput.tsx b/extensions/ql-vscode/src/view/compare-performance/RenamingInput.tsx index 6d86c7e8182..c938cc6d1a4 100644 --- a/extensions/ql-vscode/src/view/compare-performance/RenamingInput.tsx +++ b/extensions/ql-vscode/src/view/compare-performance/RenamingInput.tsx @@ -1,9 +1,6 @@ import type { ChangeEvent } from "react"; import { styled } from "styled-components"; -import { - VSCodeButton, - VSCodeTextField, -} from "@vscode/webview-ui-toolkit/react"; +import { VscodeButton, VscodeTextfield } from "@vscode-elements/react-elements"; import { Codicon } from "../common"; export class Renaming { @@ -25,7 +22,7 @@ function tryCompilePattern(pattern: string): RegExp | undefined { } } -const Input = styled(VSCodeTextField)` +const Input = styled(VscodeTextfield)` width: 20em; `; @@ -86,21 +83,21 @@ export function RenamingInput(props: RenamingInputProps) { setRenamings(newRenamings); }} > - setRenamings(renamings.filter((_, i) => i !== index)) } > - +
))} - setRenamings([...renamings, new Renaming("", "")])} > Add renaming rule - + ); } diff --git a/extensions/ql-vscode/src/view/jest.setup.ts b/extensions/ql-vscode/src/view/jest.setup.ts index 23cb3e2f973..31dc6657d40 100644 --- a/extensions/ql-vscode/src/view/jest.setup.ts +++ b/extensions/ql-vscode/src/view/jest.setup.ts @@ -18,6 +18,20 @@ Object.defineProperty(window, "matchMedia", { // Used by Primer React window.CSS.supports = jest.fn().mockResolvedValue(false); +// Functions that are not implemented in jsdom +window.CSSStyleSheet.prototype.replaceSync = jest + .fn() + .mockReturnValue(undefined); +window.ElementInternals.prototype.setFormValue = jest + .fn() + .mockReturnValue(undefined); +window.ElementInternals.prototype.setValidity = jest + .fn() + .mockReturnValue(undefined); +window.HTMLSlotElement.prototype.assignedElements = jest + .fn() + .mockReturnValue([]); + // Store this on the window so we can mock it window.vsCodeApi = { postMessage: jest.fn(), diff --git a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx index bb2fd6b2ab5..ed55c497d71 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx @@ -7,7 +7,6 @@ import { } from "../../model-editor/shared/multiple-modeled-methods"; import { styled } from "styled-components"; import { MethodModelingInputs } from "./MethodModelingInputs"; -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; import { Codicon } from "../common"; import { validateModeledMethods } from "../../model-editor/shared/validation"; import { ModeledMethodAlert } from "./ModeledMethodAlert"; @@ -15,6 +14,7 @@ import type { QueryLanguage } from "../../common/query-language"; import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty"; import { sendTelemetry } from "../common/telemetry"; import type { ModelConfig } from "../../model-editor/languages"; +import { ActionButton } from "../common/ActionButton/ActionButton"; export type MultipleModeledMethodsPanelProps = { language: QueryLanguage; @@ -168,21 +168,19 @@ export const MultipleModeledMethodsPanel = ({ )}
- - + {modeledMethods.length > 1 && (
{selectedIndex + 1}/{modeledMethods.length}
)} - - +
- - - + - +
diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx index 9c0e0b464da..27372287bfc 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx @@ -52,32 +52,26 @@ describe(MultipleModeledMethodsPanel.name, () => { ).toHaveValue("none"); }); - it("disables all pagination", () => { + it("disables all pagination", async () => { render(); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeDisabled(); + const prevButton = await screen.findByLabelText("Previous modeling"); + const nextButton = await screen.findByLabelText("Next modeling"); + + expect(prevButton).toBeDisabled(); + expect(nextButton).toBeDisabled(); expect(screen.queryByText("0/0")).not.toBeInTheDocument(); expect(screen.queryByText("1/0")).not.toBeInTheDocument(); }); - it("cannot add or delete modeling", () => { + it("cannot add or delete modeling", async () => { render(); - expect( - screen - .getByLabelText("Delete modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); - expect( - screen.getByLabelText("Add modeling").getElementsByTagName("input")[0], - ).toBeDisabled(); + const deleteButton = await screen.findByLabelText("Delete modeling"); + const addButton = await screen.findByLabelText("Add modeling"); + + expect(deleteButton).toBeDisabled(); + expect(addButton).toBeDisabled(); }); }); @@ -104,28 +98,22 @@ describe(MultipleModeledMethodsPanel.name, () => { ).toHaveValue("sink"); }); - it("disables all pagination", () => { + it("disables all pagination", async () => { render(); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeDisabled(); + const prevButton = await screen.findByLabelText("Previous modeling"); + const nextButton = await screen.findByLabelText("Next modeling"); + + expect(prevButton).toBeDisabled(); + expect(nextButton).toBeDisabled(); expect(screen.queryByText("1/1")).not.toBeInTheDocument(); }); - it("cannot delete modeling", () => { + it("cannot delete modeling", async () => { render(); - expect( - screen - .getByLabelText("Delete modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); + const deleteButton = await screen.findByLabelText("Delete modeling"); + expect(deleteButton).toBeDisabled(); }); it("can add modeling", async () => { @@ -199,37 +187,26 @@ describe(MultipleModeledMethodsPanel.name, () => { it("disables the correct pagination", async () => { render(); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeEnabled(); + const prevButton = await screen.findByLabelText("Previous modeling"); + const nextButton = await screen.findByLabelText("Next modeling"); + + expect(prevButton).toBeDisabled(); + expect(nextButton).toBeEnabled(); }); it("can use the pagination", async () => { render(); - await userEvent.click(screen.getByLabelText("Next modeling")); + const prevButton = await screen.findByLabelText("Previous modeling"); + const nextButton = await screen.findByLabelText("Next modeling"); + await userEvent.click(nextButton); await waitFor(() => { - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(prevButton).toBeEnabled(); }); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeDisabled(); + expect(prevButton).toBeEnabled(); + expect(nextButton).toBeDisabled(); expect(screen.getByText("2/2")).toBeInTheDocument(); expect( @@ -445,34 +422,20 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can use the pagination", async () => { render(); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeDisabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeEnabled(); + const prevButton = await screen.findByLabelText("Previous modeling"); + const nextButton = await screen.findByLabelText("Next modeling"); + expect(prevButton).toBeDisabled(); + expect(nextButton).toBeEnabled(); expect(screen.getByText("1/3")).toBeInTheDocument(); - await userEvent.click(screen.getByLabelText("Next modeling")); + await userEvent.click(nextButton); await waitFor(() => { - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(prevButton).toBeEnabled(); }); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(prevButton).toBeEnabled(); + expect(nextButton).toBeEnabled(); expect(screen.getByText("2/3")).toBeInTheDocument(); expect( @@ -481,16 +444,10 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ).toHaveValue("source"); - await userEvent.click(screen.getByLabelText("Next modeling")); + await userEvent.click(nextButton); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeDisabled(); + expect(prevButton).toBeEnabled(); + expect(nextButton).toBeDisabled(); expect(screen.getByText("3/3")).toBeInTheDocument(); expect( @@ -499,24 +456,14 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ).toHaveValue("local"); - await userEvent.click(screen.getByLabelText("Previous modeling")); + await userEvent.click(prevButton); await waitFor(() => { - expect( - screen - .getByLabelText("Next modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(nextButton).toBeEnabled(); }); - expect( - screen - .getByLabelText("Previous modeling") - .getElementsByTagName("input")[0], - ).toBeEnabled(); - expect( - screen.getByLabelText("Next modeling").getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(prevButton).toBeEnabled(); + expect(nextButton).toBeEnabled(); expect(screen.getByText("2/3")).toBeInTheDocument(); expect( @@ -574,12 +521,11 @@ describe(MultipleModeledMethodsPanel.name, () => { const render = createRender(modeledMethods); - it("can add modeling", () => { + it("can add modeling", async () => { render(); - expect( - screen.getByLabelText("Add modeling").getElementsByTagName("input")[0], - ).toBeEnabled(); + const addButton = await screen.findByLabelText("Add modeling"); + expect(addButton).toBeEnabled(); }); it("can delete first modeling", async () => { diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx index e99689634d2..da73126d2fc 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx @@ -1,6 +1,6 @@ import { styled } from "styled-components"; -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; +import { VscodeButton } from "@vscode-elements/react-elements"; type ModelAlertsActionsProps = { variantAnalysisStatus: VariantAnalysisStatus; @@ -14,7 +14,7 @@ const Container = styled.div` gap: 1em; `; -const Button = styled(VSCodeButton)` +const Button = styled(VscodeButton)` white-space: nowrap; `; @@ -25,12 +25,12 @@ export const ModelAlertsActions = ({ return ( {variantAnalysisStatus === VariantAnalysisStatus.InProgress && ( - )} {variantAnalysisStatus === VariantAnalysisStatus.Canceling && ( - )} diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsResults.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsResults.tsx index 8b27896d333..d10757e3748 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsResults.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsResults.tsx @@ -1,7 +1,6 @@ import { styled } from "styled-components"; import type { ModelAlerts } from "../../model-editor/model-alerts/model-alerts"; import { Codicon } from "../common"; -import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react"; import { useCallback, useEffect, useRef, useState } from "react"; import { formatDecimal } from "../../common/number"; import AnalysisAlertResult from "../variant-analysis/AnalysisAlertResult"; @@ -11,6 +10,7 @@ import { vscode } from "../vscode-api"; import { createModeledMethodKey } from "../../model-editor/modeled-method"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import { Link } from "../common/Link"; +import { Badge } from "../common/Badge"; // This will ensure that these icons have a className which we can use in the TitleContainer const ExpandCollapseCodicon = styled(Codicon)``; @@ -103,7 +103,7 @@ export const ModelAlertsResults = ({ {!isExpanded && ( )} - {formatDecimal(modelAlerts.alerts.length)} + {formatDecimal(modelAlerts.alerts.length)} {modelAlerts.model.type} { ); return ( - + - Alphabetically - + Alphabetically + Number of results - + ); }; diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index 662d739ad7b..f2f1065b4d9 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -7,11 +7,12 @@ import { calculateModeledPercentage } from "../../model-editor/shared/modeled-pe import { percentFormatter } from "./formatters"; import { Codicon } from "../common"; import { Mode } from "../../model-editor/shared/mode"; -import { VSCodeButton, VSCodeDivider } from "@vscode/webview-ui-toolkit/react"; +import { VscodeButton, VscodeDivider } from "@vscode-elements/react-elements"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; import { Tag } from "../common/Tag"; +import { ActionButton } from "../common/ActionButton/ActionButton"; const LibraryContainer = styled.div` background-color: var(--vscode-peekViewResult-background); @@ -34,9 +35,9 @@ const TitleContainer = styled.button` cursor: pointer; `; -const SectionDivider = styled(VSCodeDivider)` - padding-top: 0.3rem; - padding-bottom: 0.3rem; +const SectionDivider = styled(VscodeDivider)` + margin-top: 0.3rem; + margin-bottom: 0.8rem; `; const NameContainer = styled.div` @@ -170,16 +171,16 @@ export const LibraryRow = ({ {viewState.showGenerateButton && viewState.mode === Mode.Application && ( - +  Model from source - + )} {viewState.mode === Mode.Application && ( - +  Model dependency - + )} {isExpanded && ( @@ -200,9 +201,9 @@ export const LibraryRow = ({ /> - + {selectedSignatures.size === 0 ? "Save" : "Save selected"} - + )} diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 22dc54d2a48..6e753c3532d 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -1,4 +1,3 @@ -import { VSCodeBadge, VSCodeButton } from "@vscode/webview-ui-toolkit/react"; import { forwardRef, useCallback, @@ -10,6 +9,7 @@ import { import { styled } from "styled-components"; import { vscode } from "../vscode-api"; import { Link } from "../common/Link"; +import { ActionButton } from "../common/ActionButton/ActionButton"; import type { Method } from "../../model-editor/method"; import type { ModeledMethod } from "../../model-editor/modeled-method"; @@ -35,6 +35,7 @@ import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox"; import { getModelsAsDataLanguage } from "../../model-editor/languages"; import { ModelAlertsIndicator } from "./ModelAlertsIndicator"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; +import { Badge } from "../common/Badge"; const ApiOrMethodRow = styled.div` min-height: calc(var(--input-height) * 1px); @@ -52,15 +53,16 @@ const ModelButtonsContainer = styled.div` gap: 1em; `; -const UsagesButton = styled(VSCodeBadge)` +const UsagesButton = styled(Badge)` cursor: pointer; + display: table; `; const ViewLink = styled(Link)` white-space: nowrap; `; -const CodiconRow = styled(VSCodeButton)` +const CodiconRow = styled(ActionButton)` min-height: calc(var(--input-height) * 1px); align-items: center; `; @@ -318,7 +320,6 @@ const ModelableMethodRow = forwardRef( > {index === 0 ? ( { event.stopPropagation(); @@ -330,7 +331,6 @@ const ModelableMethodRow = forwardRef( ) : ( { event.stopPropagation(); diff --git a/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx index 117d18903ca..a99bc815f1e 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx @@ -2,10 +2,10 @@ import { styled } from "styled-components"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; -import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react"; import { vscode } from "../vscode-api"; +import { Badge } from "../common/Badge"; -const ModelAlertsButton = styled(VSCodeBadge)` +const ModelAlertsButton = styled(Badge)` cursor: pointer; `; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 6d9628cf0db..344a0d32d98 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import type { ToModelEditorMessage } from "../../common/interface-types"; -import { VSCodeButton, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"; +import { VscodeButton, VscodeCheckbox } from "@vscode-elements/react-elements"; import { styled } from "styled-components"; import type { Method } from "../../model-editor/method"; import type { ModeledMethod } from "../../model-editor/modeled-method"; @@ -331,27 +331,27 @@ export function ModelEditor({ - {selectedSignatures.size === 0 ? "Save all" : "Save selected"} - - + Deselect all - - + + Refresh - + {viewState.showGenerateButton && viewState.mode === Mode.Framework && ( - + Generate - + )} - Hide modeled methods - + diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx index 3994caddf82..523e8fd32c8 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx @@ -1,7 +1,7 @@ -import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"; +import { VscodeProgressRing } from "@vscode-elements/react-elements"; import { styled } from "styled-components"; -export const ModelEditorProgressRing = styled(VSCodeProgressRing)` +export const ModelEditorProgressRing = styled(VscodeProgressRing)` width: 16px; height: 16px; margin-right: 5px; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx index 95b94aa5411..ba0266c083f 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -1,5 +1,4 @@ import { styled } from "styled-components"; -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; @@ -7,6 +6,7 @@ import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-eva import { ModelEditorProgressRing } from "./ModelEditorProgressRing"; import { LinkIconButton } from "../common/LinkIconButton"; import { Link } from "../common/Link"; +import { VscodeButton } from "@vscode-elements/react-elements"; export type Props = { viewState: ModelEditorViewState; @@ -53,19 +53,19 @@ export const ModelEvaluation = ({ return ( <> {shouldShowEvaluateButton && ( - Evaluate - + )} {shouldShowStopButton && ( - + Stop evaluation - + )} {shouldShowEvaluationRunLink && ( diff --git a/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx b/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx index d52ab1306bf..69f9a0829b9 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx @@ -4,8 +4,8 @@ import type { ModeledMethod, TypeModeledMethod, } from "../../model-editor/modeled-method"; -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; import { useDebounceCallback } from "../common/useDebounceCallback"; +import { VscodeTextfield } from "@vscode-elements/react-elements"; type Props = { modeledMethod: TypeModeledMethod; @@ -53,7 +53,7 @@ export const ModelTypeTextbox = ({ ); return ( - { modeledMethods: [], }); - const addButton = screen.queryByLabelText("Add new model"); + const addButton = await screen.findByLabelText("Add new model"); expect(addButton).toBeInTheDocument(); - expect(addButton?.getElementsByTagName("input")[0]).toBeDisabled(); + expect(addButton).toBeDisabled(); expect(screen.queryByLabelText("Remove model")).not.toBeInTheDocument(); }); @@ -255,9 +255,9 @@ describe(MethodRow.name, () => { modeledMethods: [{ ...modeledMethod, type: "none" }], }); - const addButton = screen.queryByLabelText("Add new model"); + const addButton = await screen.findByLabelText("Add new model"); expect(addButton).toBeInTheDocument(); - expect(addButton?.getElementsByTagName("input")[0]).toBeDisabled(); + expect(addButton).toBeDisabled(); expect(screen.queryByLabelText("Remove model")).not.toBeInTheDocument(); }); @@ -267,9 +267,9 @@ describe(MethodRow.name, () => { modeledMethods: [modeledMethod], }); - const addButton = screen.queryByLabelText("Add new model"); + const addButton = await screen.findByLabelText("Add new model"); expect(addButton).toBeInTheDocument(); - expect(addButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(addButton).toBeEnabled(); expect(screen.queryByLabelText("Remove model")).not.toBeInTheDocument(); }); @@ -282,16 +282,16 @@ describe(MethodRow.name, () => { ], }); - const addButton = screen.queryByLabelText("Add new model"); + const addButton = await screen.findByLabelText("Add new model"); expect(addButton).toBeInTheDocument(); - expect(addButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(addButton).toBeEnabled(); - const removeButton = screen.queryByLabelText("Remove model"); + const removeButton = await screen.findByLabelText("Remove model"); expect(removeButton).toBeInTheDocument(); - expect(removeButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(removeButton).toBeEnabled(); }); - it("shows add model button on first row and remove model button on all other rows", async () => { + it("shows add model button on first row and remove model button on all other rows", () => { render({ modeledMethods: [ { ...modeledMethod, type: "source" }, @@ -303,12 +303,12 @@ describe(MethodRow.name, () => { const addButtons = screen.queryAllByLabelText("Add new model"); expect(addButtons.length).toBe(1); - expect(addButtons[0]?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(addButtons[0]).toBeEnabled(); const removeButtons = screen.queryAllByLabelText("Remove model"); expect(removeButtons.length).toBe(3); for (const removeButton of removeButtons) { - expect(removeButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(removeButton).toBeEnabled(); } }); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx index 24897bfa098..150ace05b53 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx @@ -1,4 +1,4 @@ -import { render as reactRender, screen } from "@testing-library/react"; +import { render as reactRender, screen, waitFor } from "@testing-library/react"; import type { Props } from "../ModelEvaluation"; import { ModelEvaluation } from "../ModelEvaluation"; import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state"; @@ -39,47 +39,51 @@ describe(ModelEvaluation.name, () => { }); describe("when showEvaluationUi is true", () => { - it("renders evaluation UI with 'Evaluate' button enabled", () => { + it("renders evaluation UI with 'Evaluate' button enabled", async () => { render(); - const evaluateButton = screen.queryByText("Evaluate"); + const evaluateButton = await screen.findByText("Evaluate"); expect(evaluateButton).toBeInTheDocument(); - expect(evaluateButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(evaluateButton).toBeEnabled(); expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); }); - it("disables 'Evaluate' button when there are no custom models", () => { + it("disables 'Evaluate' button when there are no custom models", async () => { render({ modeledMethods: {}, }); - const evaluateButton = screen.queryByText("Evaluate"); + const evaluateButton = await screen.findByText("Evaluate"); expect(evaluateButton).toBeInTheDocument(); - expect(evaluateButton?.getElementsByTagName("input")[0]).toBeDisabled(); + await waitFor(() => { + expect(evaluateButton).toBeDisabled(); + }); expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); }); - it("disables 'Evaluate' button when there are unsaved changes", () => { + it("disables 'Evaluate' button when there are unsaved changes", async () => { render({ modifiedSignatures: new Set([method.signature]), }); - const evaluateButton = screen.queryByText("Evaluate"); + const evaluateButton = await screen.findByText("Evaluate"); expect(evaluateButton).toBeInTheDocument(); - expect(evaluateButton?.getElementsByTagName("input")[0]).toBeDisabled(); + await waitFor(() => { + expect(evaluateButton).toBeDisabled(); + }); expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); }); - it("renders 'Evaluate' button and 'Evaluation run' link when there is a completed evaluation", () => { + it("renders 'Evaluate' button and 'Evaluation run' link when there is a completed evaluation", async () => { render({ evaluationRun: { isPreparing: false, @@ -89,16 +93,16 @@ describe(ModelEvaluation.name, () => { }, }); - const evaluateButton = screen.queryByText("Evaluate"); + const evaluateButton = await screen.findByText("Evaluate"); expect(evaluateButton).toBeInTheDocument(); - expect(evaluateButton?.getElementsByTagName("input")[0]).toBeEnabled(); + expect(evaluateButton).toBeEnabled(); expect(screen.queryByText("Evaluation run")).toBeInTheDocument(); expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); }); - it("renders 'Stop evaluation' button when there is an in progress evaluation, but no variant analysis yet", () => { + it("renders 'Stop evaluation' button when there is an in progress evaluation, but no variant analysis yet", async () => { render({ evaluationRun: { isPreparing: true, @@ -106,18 +110,16 @@ describe(ModelEvaluation.name, () => { }, }); - const stopEvaluationButton = screen.queryByText("Stop evaluation"); + const stopEvaluationButton = await screen.findByText("Stop evaluation"); expect(stopEvaluationButton).toBeInTheDocument(); - expect( - stopEvaluationButton?.getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(stopEvaluationButton).toBeEnabled(); expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); expect(screen.queryByText("Evaluate")).not.toBeInTheDocument(); }); - it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation with variant analysis", () => { + it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation with variant analysis", async () => { render({ evaluationRun: { isPreparing: false, @@ -127,11 +129,9 @@ describe(ModelEvaluation.name, () => { }, }); - const stopEvaluationButton = screen.queryByText("Stop evaluation"); + const stopEvaluationButton = await screen.findByText("Stop evaluation"); expect(stopEvaluationButton).toBeInTheDocument(); - expect( - stopEvaluationButton?.getElementsByTagName("input")[0], - ).toBeEnabled(); + expect(stopEvaluationButton).toBeEnabled(); expect(screen.queryByText("Evaluation run")).toBeInTheDocument(); diff --git a/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx b/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx index 3ae0e5badac..3ab31ed3f7f 100644 --- a/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx +++ b/extensions/ql-vscode/src/view/results/AlertTableResultRow.tsx @@ -13,7 +13,7 @@ import { SarifLocation } from "./locations/SarifLocation"; import { SarifMessageWithLocations } from "./locations/SarifMessageWithLocations"; import { AlertTablePathRow } from "./AlertTablePathRow"; import type { UserSettings } from "../../common/interface-types"; -import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react"; +import { Badge } from "../common/Badge"; export interface Props { result: Result; @@ -109,7 +109,7 @@ export function AlertTableResultRow(props: Props) { /> {listUnordered} - {shortestPath} + {shortestPath} {msg} diff --git a/extensions/ql-vscode/src/view/results/__tests__/AlertTablePathRow.spec.tsx b/extensions/ql-vscode/src/view/results/__tests__/AlertTablePathRow.spec.tsx index ada710bee85..001fa322a34 100644 --- a/extensions/ql-vscode/src/view/results/__tests__/AlertTablePathRow.spec.tsx +++ b/extensions/ql-vscode/src/view/results/__tests__/AlertTablePathRow.spec.tsx @@ -22,7 +22,9 @@ describe(AlertTablePathRow.name, () => { currentPathExpanded={true} databaseUri={"dbUri"} sourceLocationPrefix="src" - userSettings={{ shouldShowProvenance: false }} + userSettings={{ + shouldShowProvenance: false, + }} updateSelectionCallback={jest.fn()} toggleExpanded={jest.fn()} {...props} diff --git a/extensions/ql-vscode/src/view/results/__tests__/AlertTableResultRow.spec.tsx b/extensions/ql-vscode/src/view/results/__tests__/AlertTableResultRow.spec.tsx index 0aa7279ae72..9082384e9d2 100644 --- a/extensions/ql-vscode/src/view/results/__tests__/AlertTableResultRow.spec.tsx +++ b/extensions/ql-vscode/src/view/results/__tests__/AlertTableResultRow.spec.tsx @@ -17,7 +17,9 @@ describe(AlertTableResultRow.name, () => { selectedItemRef={mockRef} databaseUri={"dbUri"} sourceLocationPrefix="src" - userSettings={{ shouldShowProvenance: false }} + userSettings={{ + shouldShowProvenance: false, + }} updateSelectionCallback={jest.fn()} toggleExpanded={jest.fn()} {...props} diff --git a/extensions/ql-vscode/src/view/tsconfig.json b/extensions/ql-vscode/src/view/tsconfig.json index 4abd2c7f8a9..d094c039d78 100644 --- a/extensions/ql-vscode/src/view/tsconfig.json +++ b/extensions/ql-vscode/src/view/tsconfig.json @@ -14,7 +14,12 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "experimentalDecorators": true, - "skipLibCheck": true + "skipLibCheck": true, + "plugins": [ + { + "name": "typescript-plugin-css-modules" + } + ] }, "exclude": ["node_modules"] } diff --git a/extensions/ql-vscode/src/view/types.d.ts b/extensions/ql-vscode/src/view/types.d.ts new file mode 100644 index 00000000000..1eabbb4297e --- /dev/null +++ b/extensions/ql-vscode/src/view/types.d.ts @@ -0,0 +1 @@ +declare module "*.module.css"; diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx index fc381fe016f..19edad08cc2 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx @@ -1,7 +1,7 @@ import type { ChangeEvent } from "react"; import { useCallback, useEffect, useState } from "react"; import { styled } from "styled-components"; -import { VSCodeBadge, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"; +import { VscodeCheckbox } from "@vscode-elements/react-elements"; import type { VariantAnalysisScannedRepositoryState } from "../../variant-analysis/shared/variant-analysis"; import { isCompletedAnalysisRepoStatus, @@ -27,6 +27,7 @@ import StarCount from "../common/StarCount"; import { useTelemetryOnChange } from "../common/telemetry"; import { DeterminateProgressRing } from "../common/DeterminateProgressRing"; import { ResultFormat } from "../../variant-analysis/shared/variant-analysis-result-format"; +import { Badge } from "../common/Badge"; // This will ensure that these icons have a className which we can use in the TitleContainer const ExpandCollapseCodicon = styled(Codicon)``; @@ -61,6 +62,10 @@ const MetadataContainer = styled.div` margin-left: auto; `; +const Checkbox = styled(VscodeCheckbox)` + margin-right: -9px; // VscodeCheckbox has 9px margin on the right by default +`; + type VisibilityProps = { isPrivate?: boolean; }; @@ -254,7 +259,7 @@ export const RepoRow = ({ disabled={disabled} aria-expanded={isExpanded} > - )} {resultsLoading && } - + {resultCount === undefined ? "-" : formatDecimal(resultCount)} - + {repository.fullName} diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesFilter.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesFilter.tsx index 56596b2ecbf..206b33586fb 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesFilter.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesFilter.tsx @@ -1,10 +1,13 @@ import { useCallback } from "react"; import { styled } from "styled-components"; -import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"; +import { + VscodeOption, + VscodeSingleSelect, +} from "@vscode-elements/react-elements"; import { Codicon } from "../common"; import { FilterKey } from "../../variant-analysis/shared/variant-analysis-filter-sort"; -const Dropdown = styled(VSCodeDropdown)` +const Dropdown = styled(VscodeSingleSelect)` width: 100%; `; @@ -26,10 +29,10 @@ export const RepositoriesFilter = ({ value, onChange, className }: Props) => { ); return ( - + - All - With results + All + With results ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesResultFormat.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesResultFormat.tsx index 11ac814cc18..380599017eb 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesResultFormat.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesResultFormat.tsx @@ -1,10 +1,13 @@ import { useCallback } from "react"; import { styled } from "styled-components"; -import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"; +import { + VscodeOption, + VscodeSingleSelect, +} from "@vscode-elements/react-elements"; import { Codicon } from "../common"; import { ResultFormat } from "../../variant-analysis/shared/variant-analysis-result-format"; -const Dropdown = styled(VSCodeDropdown)` +const Dropdown = styled(VscodeSingleSelect)` width: 100%; `; @@ -30,14 +33,14 @@ export const RepositoriesResultFormat = ({ ); return ( - + - + {ResultFormat.Alerts} - - + + {ResultFormat.RawResults} - + ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSort.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSort.tsx index d4383a3e1ea..96ec4c43dd4 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSort.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSort.tsx @@ -1,10 +1,13 @@ import { useCallback } from "react"; import { styled } from "styled-components"; -import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"; +import { + VscodeOption, + VscodeSingleSelect, +} from "@vscode-elements/react-elements"; import { SortKey } from "../../variant-analysis/shared/variant-analysis-filter-sort"; import { Codicon } from "../common"; -const Dropdown = styled(VSCodeDropdown)` +const Dropdown = styled(VscodeSingleSelect)` width: 100%; `; @@ -26,13 +29,13 @@ export const RepositoriesSort = ({ value, onChange, className }: Props) => { ); return ( - + - Alphabetically - + Alphabetically + Number of results - - Popularity + + Popularity ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx index 5b68d114d6f..3ac0599ae6c 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx @@ -9,11 +9,15 @@ import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-ana import { VariantAnalysisHeader } from "./VariantAnalysisHeader"; import { VariantAnalysisOutcomePanels } from "./VariantAnalysisOutcomePanels"; import { VariantAnalysisLoading } from "./VariantAnalysisLoading"; -import type { ToVariantAnalysisMessage } from "../../common/interface-types"; +import type { + ToVariantAnalysisMessage, + VariantAnalysisUserSettings, +} from "../../common/interface-types"; import { vscode } from "../vscode-api"; import { defaultFilterSortState } from "../../variant-analysis/shared/variant-analysis-filter-sort"; import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry"; import { useMessageFromExtension } from "../common/useMessageFromExtension"; +import { DEFAULT_VARIANT_ANALYSIS_USER_SETTINGS } from "../../common/interface-types"; export type VariantAnalysisProps = { variantAnalysis?: VariantAnalysisDomainModel; @@ -77,6 +81,10 @@ export function VariantAnalysis({ useTelemetryOnChange(filterSortState, "variant-analysis-filter-sort-state", { debounceTimeoutMillis: 1000, }); + const [variantAnalysisUserSettings, setVariantAnalysisUserSettings] = + useState( + DEFAULT_VARIANT_ANALYSIS_USER_SETTINGS, + ); useMessageFromExtension((msg) => { if (msg.t === "setVariantAnalysis") { @@ -102,9 +110,22 @@ export function VariantAnalysis({ ...msg.repoStates, ]; }); + } else if (msg.t === "setVariantAnalysisUserSettings") { + setVariantAnalysisUserSettings(msg.variantAnalysisUserSettings); } }, []); + const viewAutofixes = useCallback(() => { + vscode.postMessage({ + t: "viewAutofixes", + filterSort: { + ...filterSortState, + repositoryIds: selectedRepositoryIds, + }, + }); + sendTelemetry("variant-analysis-view-autofixes"); + }, [filterSortState, selectedRepositoryIds]); + const copyRepositoryList = useCallback(() => { vscode.postMessage({ t: "copyRepositoryList", @@ -148,9 +169,11 @@ export function VariantAnalysis({ onOpenQueryFileClick={openQueryFile} onViewQueryTextClick={openQueryText} onStopQueryClick={stopQuery} + onViewAutofixesClick={viewAutofixes} onCopyRepositoryListClick={copyRepositoryList} onExportResultsClick={exportResults} onViewLogsClick={onViewLogsClick} + variantAnalysisUserSettings={variantAnalysisUserSettings} /> void; onCopyRepositoryListClick: () => void; onExportResultsClick: () => void; + viewAutofixesDisabled?: boolean; copyRepositoryListDisabled?: boolean; exportResultsDisabled?: boolean; hasSelectedRepositories?: boolean; hasFilteredRepositories?: boolean; + + showViewAutofixesButton: boolean; }; const Container = styled.div` @@ -24,7 +28,7 @@ const Container = styled.div` gap: 1em; `; -const Button = styled(VSCodeButton)` +const Button = styled(VscodeButton)` white-space: nowrap; `; @@ -55,19 +59,37 @@ export const VariantAnalysisActions = ({ onStopQueryClick, stopQueryDisabled, showResultActions, + onViewAutofixesClick, onCopyRepositoryListClick, onExportResultsClick, + viewAutofixesDisabled, copyRepositoryListDisabled, exportResultsDisabled, hasSelectedRepositories, hasFilteredRepositories, + showViewAutofixesButton, }: VariantAnalysisActionsProps) => { return ( {showResultActions && ( <> + {showViewAutofixesButton && ( + + )} )} {variantAnalysisStatus === VariantAnalysisStatus.Canceling && ( - )} diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx index bdd9b5e11be..6f9ad00ad33 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisHeader.tsx @@ -22,6 +22,7 @@ import { filterAndSortRepositoriesWithResults, } from "../../variant-analysis/shared/variant-analysis-filter-sort"; import { ViewTitle } from "../common"; +import type { VariantAnalysisUserSettings } from "../../common/interface-types"; type VariantAnalysisHeaderProps = { variantAnalysis: VariantAnalysis; @@ -34,10 +35,13 @@ type VariantAnalysisHeaderProps = { onStopQueryClick: () => void; + onViewAutofixesClick: () => void; onCopyRepositoryListClick: () => void; onExportResultsClick: () => void; onViewLogsClick?: () => void; + + variantAnalysisUserSettings: VariantAnalysisUserSettings; }; const Container = styled.div` @@ -82,9 +86,11 @@ export const VariantAnalysisHeader = ({ onOpenQueryFileClick, onViewQueryTextClick, onStopQueryClick, + onViewAutofixesClick, onCopyRepositoryListClick, onExportResultsClick, onViewLogsClick, + variantAnalysisUserSettings, }: VariantAnalysisHeaderProps) => { const totalScannedRepositoryCount = useMemo(() => { return variantAnalysis.scannedRepos?.length ?? 0; @@ -150,11 +156,13 @@ export const VariantAnalysisHeader = ({ variantAnalysisStatus={variantAnalysis.status} showResultActions={(resultCount ?? 0) > 0} onStopQueryClick={onStopQueryClick} + onViewAutofixesClick={onViewAutofixesClick} onCopyRepositoryListClick={onCopyRepositoryListClick} onExportResultsClick={onExportResultsClick} stopQueryDisabled={!variantAnalysis.actionsWorkflowRunId} exportResultsDisabled={!hasDownloadedRepos} copyRepositoryListDisabled={!hasReposWithResults} + viewAutofixesDisabled={!hasReposWithResults} hasFilteredRepositories={ variantAnalysis.scannedRepos?.length !== filteredRepositories?.length @@ -162,6 +170,9 @@ export const VariantAnalysisHeader = ({ hasSelectedRepositories={ selectedRepositoryIds && selectedRepositoryIds.length > 0 } + showViewAutofixesButton={ + variantAnalysisUserSettings.shouldShowViewAutofixesButton + } /> >; }; -const Tab = styled(VSCodePanelTab)` +const Tabs = styled(VscodeTabs)` + column-gap: 32px; + + > vscode-tab-header { + margin-right: 32px; + } +`; + +const TabHeader = styled(VscodeTabHeader)` text-transform: uppercase; + + > * { + // This copies the styles from @vscode/webview-ui-toolkit's VSCodePanelTab + &:last-child { + margin-left: 8px; + } + } +`; + +const TabPanel = styled(VscodeTabPanel)` + padding: 10px 6px; `; const WarningsContainer = styled.div` @@ -154,33 +173,31 @@ export const VariantAnalysisOutcomePanels = ({ onResultFormatChange={setResultFormat} variantAnalysisQueryKind={variantAnalysis.query.kind} /> - + {scannedReposCount > 0 && ( - + Analyzed - + {formatDecimal(variantAnalysis.scannedRepos?.length ?? 0)} - - - )} - {notFoundRepos?.repositoryCount && ( - - No access - - {formatDecimal(notFoundRepos.repositoryCount)} - - - )} - {noCodeqlDbRepos?.repositoryCount && ( - - No database - - {formatDecimal(noCodeqlDbRepos.repositoryCount)} - - + + )} + {notFoundRepos?.repositoryCount !== undefined && + notFoundRepos?.repositoryCount > 0 && ( + + No access + {formatDecimal(notFoundRepos.repositoryCount)} + + )} + {noCodeqlDbRepos?.repositoryCount !== undefined && + noCodeqlDbRepos?.repositoryCount > 0 && ( + + No database + {formatDecimal(noCodeqlDbRepos.repositoryCount)} + + )} {scannedReposCount > 0 && ( - + - - )} - {notFoundRepos?.repositoryCount && ( - - - - )} - {noCodeqlDbRepos?.repositoryCount && ( - - - + )} - + {notFoundRepos?.repositoryCount !== undefined && + notFoundRepos?.repositoryCount > 0 && ( + + + + )} + {noCodeqlDbRepos?.repositoryCount !== undefined && + noCodeqlDbRepos?.repositoryCount > 0 && ( + + + + )} + ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx index b1f4df59a71..1c89400615d 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/RepoRow.spec.tsx @@ -1,9 +1,4 @@ -import { - act, - render as reactRender, - screen, - waitFor, -} from "@testing-library/react"; +import { act, render as reactRender, screen } from "@testing-library/react"; import { VariantAnalysisRepoStatus, VariantAnalysisScannedRepositoryDownloadStatus, @@ -403,7 +398,8 @@ describe(RepoRow.name, () => { status: VariantAnalysisRepoStatus.InProgress, }); - expect(screen.getByRole("checkbox")).toBeDisabled(); + const checkbox = await screen.findByRole("checkbox"); + expect(checkbox).toBeDisabled(); }); it("does not allow selecting the item if the item has not been downloaded", async () => { @@ -411,7 +407,8 @@ describe(RepoRow.name, () => { status: VariantAnalysisRepoStatus.Succeeded, }); - expect(screen.getByRole("checkbox")).toBeDisabled(); + const checkbox = await screen.findByRole("checkbox"); + expect(checkbox).toBeDisabled(); }); it("does not allow selecting the item if the item has not been downloaded successfully", async () => { @@ -423,11 +420,8 @@ describe(RepoRow.name, () => { }, }); - // It seems like sometimes the first render doesn't have the checkbox disabled - // Might be related to https://github.com/microsoft/vscode-webview-ui-toolkit/issues/404 - await waitFor(() => { - expect(screen.getByRole("checkbox")).toBeDisabled(); - }); + const checkbox = await screen.findByRole("checkbox"); + expect(checkbox).toBeDisabled(); }); it("allows selecting the item if the item has been downloaded", async () => { @@ -440,6 +434,7 @@ describe(RepoRow.name, () => { }, }); - expect(screen.getByRole("checkbox")).toBeEnabled(); + const checkbox = await screen.findByRole("checkbox"); + expect(checkbox).toBeEnabled(); }); }); diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx index b8a7bfcd539..3279ed446d2 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysis.spec.tsx @@ -1,4 +1,4 @@ -import { render as reactRender, screen, waitFor } from "@testing-library/react"; +import { render as reactRender, screen } from "@testing-library/react"; import { VariantAnalysisFailureReason, VariantAnalysisStatus, @@ -57,9 +57,6 @@ describe(VariantAnalysis.name, () => { const variantAnalysis = createMockVariantAnalysis({}); render({ variantAnalysis }); - await waitFor(() => screen.getByDisplayValue("All")); - await waitFor(() => screen.getByDisplayValue("Number of results")); - await postMessage({ t: "setFilterSortState", filterSortState: { @@ -69,8 +66,11 @@ describe(VariantAnalysis.name, () => { }, }); - expect(screen.getByDisplayValue("With results")).toBeInTheDocument(); - expect(screen.getByDisplayValue("Alphabetically")).toBeInTheDocument(); + const withResults = await screen.findByText("With results"); + expect(withResults).toBeInTheDocument(); + + const alphabetically = await screen.findByText("Alphabetically"); + expect(alphabetically).toBeInTheDocument(); expect(screen.queryByDisplayValue("All")).not.toBeInTheDocument(); expect( diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx index de19458646e..08d3fe84b54 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx @@ -6,11 +6,13 @@ import { VariantAnalysisActions } from "../VariantAnalysisActions"; describe(VariantAnalysisActions.name, () => { const onStopQueryClick = jest.fn(); + const onViewAutofixesClick = jest.fn(); const onCopyRepositoryListClick = jest.fn(); const onExportResultsClick = jest.fn(); afterEach(() => { onStopQueryClick.mockReset(); + onViewAutofixesClick.mockReset(); onCopyRepositoryListClick.mockReset(); onExportResultsClick.mockReset(); }); @@ -22,8 +24,10 @@ describe(VariantAnalysisActions.name, () => { reactRender( , ); @@ -50,9 +54,9 @@ describe(VariantAnalysisActions.name, () => { variantAnalysisStatus: VariantAnalysisStatus.Canceling, }); - const button = screen.getByText("Stopping query"); + const button = await screen.findByText("Stopping query"); expect(button).toBeInTheDocument(); - expect(button.getElementsByTagName("input")[0]).toBeDisabled(); + expect(button).toBeDisabled(); }); it("does not render a stop query button when canceling", async () => { diff --git a/extensions/ql-vscode/supported_cli_versions.json b/extensions/ql-vscode/supported_cli_versions.json index 7c7d6b4cb12..0c955173959 100644 --- a/extensions/ql-vscode/supported_cli_versions.json +++ b/extensions/ql-vscode/supported_cli_versions.json @@ -1,6 +1,7 @@ [ + "v2.22.3", + "v2.21.4", "v2.20.7", "v2.19.4", - "v2.18.4", "nightly" ] diff --git a/extensions/ql-vscode/test/data-extensions/pack-using-extensions/codeql-pack.lock.yml b/extensions/ql-vscode/test/data-extensions/pack-using-extensions/codeql-pack.lock.yml new file mode 100644 index 00000000000..61121d6d0cf --- /dev/null +++ b/extensions/ql-vscode/test/data-extensions/pack-using-extensions/codeql-pack.lock.yml @@ -0,0 +1,26 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/dataflow: + version: 2.0.7 + codeql/javascript-all: + version: 2.6.3 + codeql/mad: + version: 1.0.23 + codeql/regex: + version: 1.0.23 + codeql/ssa: + version: 1.1.2 + codeql/threat-models: + version: 1.0.23 + codeql/tutorial: + version: 1.0.23 + codeql/typetracking: + version: 2.0.7 + codeql/util: + version: 2.0.10 + codeql/xml: + version: 1.0.23 + codeql/yaml: + version: 1.0.23 +compiled: false diff --git a/extensions/ql-vscode/test/e2e/docker/Dockerfile b/extensions/ql-vscode/test/e2e/docker/Dockerfile index 0f1e537538f..cea6ba9293f 100644 --- a/extensions/ql-vscode/test/e2e/docker/Dockerfile +++ b/extensions/ql-vscode/test/e2e/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM codercom/code-server:4.98.2 +FROM codercom/code-server:4.102.2 USER root diff --git a/extensions/ql-vscode/test/jest-config.ts b/extensions/ql-vscode/test/jest-config.ts index 5a6b6f64bef..3270067df7a 100644 --- a/extensions/ql-vscode/test/jest-config.ts +++ b/extensions/ql-vscode/test/jest-config.ts @@ -1,8 +1,13 @@ // These are all the packages that DO need to be transformed. All other packages will be ignored. // These pacakges all use ES modules, so need to be transformed -const transformScopes = ["@microsoft", "@octokit"]; +const transformScopes = [ + "@microsoft", + "@octokit", + "@vscode-elements", + "@lit", + "@lit-labs", +]; const transformPackages = [ - "@vscode/webview-ui-toolkit", "before-after-hook", "d3", "data-uri-to-buffer", @@ -11,13 +16,14 @@ const transformPackages = [ "fetch-blob", "formdata-polyfill", "internmap", + "lit", "nanoid", "p-queue", "p-timeout", "robust-predicates", "universal-user-agent", ]; -const transformWildcards = ["d3-(.*)"]; +const transformWildcards = ["d3-(.*)", "lit-(.*)"]; const transformPatterns = [ ...transformScopes.map((scope) => `${scope}/.+`), ...transformPackages, diff --git a/extensions/ql-vscode/test/mock-memento.ts b/extensions/ql-vscode/test/mock-memento.ts index d5f12ec5c38..24e9ea8362b 100644 --- a/extensions/ql-vscode/test/mock-memento.ts +++ b/extensions/ql-vscode/test/mock-memento.ts @@ -17,7 +17,7 @@ class MockMemento implements Memento { public get(key: string): T | undefined; public get(key: string, defaultValue: T): T; - public get(key: any, defaultValue?: any): T | T | undefined { + public get(key: any, defaultValue?: any): T | undefined { return this.map.get(key) || defaultValue; } diff --git a/extensions/ql-vscode/test/unit-tests/common/errors.test.ts b/extensions/ql-vscode/test/unit-tests/common/errors.test.ts index ca9e01de37e..0f9cabf5c37 100644 --- a/extensions/ql-vscode/test/unit-tests/common/errors.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/errors.test.ts @@ -37,7 +37,7 @@ describe("errorMessage", () => { myRealFunction(); fail("Expected an error to be thrown"); - } catch (e: unknown) { + } catch (e) { if (!(e instanceof Error)) { throw new Error("Expected an Error to be thrown"); } diff --git a/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts b/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts index 4623c62a523..18590963a86 100644 --- a/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts @@ -39,7 +39,7 @@ describe("Invocation rate limiter", () => { * value (`undefined`) with the given key. * @return The stored value or the defaultValue. */ - get(key: string, defaultValue?: T): T { + get(key: string, defaultValue?: T): T | undefined { return this.map.has(key) ? this.map.get(key) : defaultValue; } diff --git a/extensions/ql-vscode/test/unit-tests/data/evaluator-log-summaries/bad-join-order.jsonl b/extensions/ql-vscode/test/unit-tests/data/evaluator-log-summaries/bad-join-order.jsonl index 8d366067387..59d6e67f64a 100644 --- a/extensions/ql-vscode/test/unit-tests/data/evaluator-log-summaries/bad-join-order.jsonl +++ b/extensions/ql-vscode/test/unit-tests/data/evaluator-log-summaries/bad-join-order.jsonl @@ -11458,7 +11458,7 @@ "duplicationPercentages" : [ 0, -1, 1, 0, -1, 0, -1, 0, 0, -1, 1, 0, 1, -1, 1, 0 ] }, { "raReference" : "order_500000", - "counts" : [ 0, -1, 0, -1, 3138, -1, 3138, 3138, -1, 0, -1, 0, 0, -1, 0, -1, 133, 133, -1, 0, 0, -1, 133, 133, 3271, 3271, 3271 ], + "counts" : [ 0, -1, 0, -1, 31380000, -1, 3138, 3138, -1, 0, -1, 0, 0, -1, 0, -1, 133, 133, -1, 0, 0, -1, 133, 133, 3271, 3271, 3271 ], "duplicationPercentages" : [ 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 1 ] }, { "raReference" : "order_500000", diff --git a/extensions/ql-vscode/test/unit-tests/log-scanner.test.ts b/extensions/ql-vscode/test/unit-tests/log-scanner.test.ts index 3d49f3362a1..0012cecda23 100644 --- a/extensions/ql-vscode/test/unit-tests/log-scanner.test.ts +++ b/extensions/ql-vscode/test/unit-tests/log-scanner.test.ts @@ -1,28 +1,39 @@ +import { scanAndReportJoinOrderProblems } from "../../src/log-insights/join-order"; import type { EvaluationLogProblemReporter } from "../../src/log-insights/log-scanner"; -import { EvaluationLogScannerSet } from "../../src/log-insights/log-scanner"; -import { JoinOrderScannerProvider } from "../../src/log-insights/join-order"; import { join } from "path"; interface TestProblem { predicateName: string; raHash: string; - iteration: number; + order: string | undefined; message: string; } class TestProblemReporter implements EvaluationLogProblemReporter { public readonly problems: TestProblem[] = []; - public reportProblem( + public reportProblemNonRecursive( predicateName: string, raHash: string, - iteration: number, message: string, ): void { this.problems.push({ predicateName, raHash, - iteration, + order: undefined, + message, + }); + } + public reportProblemForRecursionSummary( + predicateName: string, + raHash: string, + order: string, + message: string, + ): void { + this.problems.push({ + predicateName, + raHash, + order, message, }); } @@ -34,23 +45,33 @@ class TestProblemReporter implements EvaluationLogProblemReporter { describe("log scanners", () => { it("should detect bad join orders", async () => { - const scanners = new EvaluationLogScannerSet(); - scanners.registerLogScannerProvider(new JoinOrderScannerProvider(() => 50)); const summaryPath = join( __dirname, "data/evaluator-log-summaries/bad-join-order.jsonl", ); const problemReporter = new TestProblemReporter(); - await scanners.scanLog(summaryPath, problemReporter); + await scanAndReportJoinOrderProblems(summaryPath, problemReporter, 50); + + expect(problemReporter.problems.length).toBe(2); - expect(problemReporter.problems.length).toBe(1); - expect(problemReporter.problems[0].predicateName).toBe("#select#ff"); + expect(problemReporter.problems[0].predicateName).toBe( + "Enclosing::exprEnclosingElement#c50c5fbf#ff", + ); expect(problemReporter.problems[0].raHash).toBe( - "1bb43c97jpmuh8r2v0f9hktim63", + "7cc60wtoigvl1lheqqa12d8fmi4", ); - expect(problemReporter.problems[0].iteration).toBe(0); + expect(problemReporter.problems[0].order).toBe("order_500000"); expect(problemReporter.problems[0].message).toBe( - "Relation '#select#ff' has an inefficient join order. Its join order metric is 4961.83, which is larger than the threshold of 50.00.", + "The order_500000 pipeline for 'Enclosing::exprEnclosingElement#c50c5fbf#ff@7cc60wto' has an inefficient join order. Its join order metric is 98.07, which is larger than the threshold of 50.00.", + ); + + expect(problemReporter.problems[1].predicateName).toBe("#select#ff"); + expect(problemReporter.problems[1].raHash).toBe( + "1bb43c97jpmuh8r2v0f9hktim63", + ); + expect(problemReporter.problems[1].order).toBeUndefined(); + expect(problemReporter.problems[1].message).toBe( + "'#select#ff@1bb43c97' has an inefficient join order. Its join order metric is 4961.83, which is larger than the threshold of 50.00.", ); }); }); diff --git a/extensions/ql-vscode/test/unit-tests/sarif-processing.test.ts b/extensions/ql-vscode/test/unit-tests/sarif-processing.test.ts index 987e21e03fa..39ebf8a2c83 100644 --- a/extensions/ql-vscode/test/unit-tests/sarif-processing.test.ts +++ b/extensions/ql-vscode/test/unit-tests/sarif-processing.test.ts @@ -89,7 +89,7 @@ describe("SARIF processing", () => { const rule = tryGetRule(sarifRun, result); expect(rule).toBeTruthy(); - expect(rule!.id).toBe(result!.rule!.id); + expect(rule!.id).toBe(result.rule!.id); }); }); @@ -455,7 +455,7 @@ describe("SARIF processing", () => { it("should return errors for results that have no message", () => { const sarif = buildValidSarifLog(); - sarif.runs![0]!.results![0]!.message.text = undefined; + sarif.runs[0].results![0].message.text = undefined; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -466,7 +466,7 @@ describe("SARIF processing", () => { it("should not return errors for result locations with no snippet", () => { const sarif = buildValidSarifLog(); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.contextRegion!.snippet = + sarif.runs[0].results![0].locations![0].physicalLocation!.contextRegion!.snippet = undefined; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -486,7 +486,7 @@ describe("SARIF processing", () => { it("should use highlightedRegion for result locations with no contextRegion", () => { const sarif = buildValidSarifLog(); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.contextRegion = + sarif.runs[0].results![0].locations![0].physicalLocation!.contextRegion = undefined; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -506,7 +506,7 @@ describe("SARIF processing", () => { it("should not return errors for result locations with no region", () => { const sarif = buildValidSarifLog(); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.region = + sarif.runs[0].results![0].locations![0].physicalLocation!.region = undefined; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -518,7 +518,7 @@ describe("SARIF processing", () => { it("should return errors for result locations with no physical location", () => { const sarif = buildValidSarifLog(); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.artifactLocation = + sarif.runs[0].results![0].locations![0].physicalLocation!.artifactLocation = undefined; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -642,8 +642,8 @@ describe("SARIF processing", () => { const sarif = buildValidSarifLog(); const messageText = "This shell command depends on an uncontrolled [absolute path](1)."; - sarif.runs![0]!.results![0]!.message!.text = messageText; - sarif.runs![0]!.results![0].relatedLocations = [ + sarif.runs[0].results![0].message.text = messageText; + sarif.runs[0].results![0].relatedLocations = [ { id: 1, physicalLocation: { @@ -694,7 +694,7 @@ describe("SARIF processing", () => { const sarif = buildValidSarifLog(); // Build string of 10 kilobytes const snippet = new Array(10 * 1024).fill("a").join(""); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.contextRegion!.snippet = + sarif.runs[0].results![0].locations![0].physicalLocation!.contextRegion!.snippet = { text: snippet, }; @@ -712,11 +712,11 @@ describe("SARIF processing", () => { const sarif = buildValidSarifLog(); // Build string of 10 kilobytes const snippet = new Array(10 * 1024).fill("a").join(""); - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.contextRegion!.snippet = + sarif.runs[0].results![0].locations![0].physicalLocation!.contextRegion!.snippet = { text: snippet, }; - sarif.runs![0]!.results![0]!.locations![0]!.physicalLocation!.region!.endColumn = 1000; + sarif.runs[0].results![0].locations![0].physicalLocation!.region!.endColumn = 1000; const result = extractAnalysisAlerts(sarif, fakefileLinkPrefix); @@ -729,8 +729,8 @@ describe("SARIF processing", () => { it("should be able to handle when a location has no uri", () => { const sarif = buildValidSarifLog(); - sarif.runs![0].results![0].message.text = "message [String](1)"; - sarif.runs![0].results![0].relatedLocations = [ + sarif.runs[0].results![0].message.text = "message [String](1)"; + sarif.runs[0].results![0].relatedLocations = [ { id: 1, physicalLocation: { diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index 598e3e159f9..850e221d84f 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -50,6 +50,7 @@ import { createMockVariantAnalysisConfig } from "../../../factories/config"; import { setupServer } from "msw/node"; import type { RequestHandler } from "msw"; import { http } from "msw"; +import { getErrorMessage } from "../../../../src/common/helpers-pure"; // up to 3 minutes per test jest.setTimeout(3 * 60 * 1000); @@ -622,8 +623,8 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.cancelVariantAnalysis( variantAnalysis.id + 100, ); - } catch (error: any) { - expect(error.message).toBe( + } catch (error) { + expect(getErrorMessage(error)).toBe( `No variant analysis with id: ${variantAnalysis.id + 100}`, ); } @@ -637,8 +638,8 @@ describe("Variant Analysis Manager", () => { try { await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); - } catch (error: any) { - expect(error.message).toBe( + } catch (error) { + expect(getErrorMessage(error)).toBe( `No workflow run id for variant analysis with id: ${variantAnalysis.id}`, ); } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debug-controller.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debug-controller.ts index 35435b1a797..86da827d88d 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debug-controller.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debug-controller.ts @@ -83,14 +83,17 @@ class Tracker implements DebugAdapterTracker { kind: "evaluationCompleted", started: this.started!, results: { - ...this.started!, - ...this.completed!, + id: this.started!.id, + results: new Map([[this.queryPath!, this.completed]]), outputDir: new QueryOutputDir(this.started!.outputDir), - queryTarget: { - queryPath: this.queryPath!, - quickEvalPosition: - this.started!.quickEvalContext?.quickEvalPosition, - }, + queryTargets: [ + { + queryPath: this.queryPath!, + outputBaseName: "results", + quickEvalPosition: + this.started!.quickEvalContext?.quickEvalPosition, + }, + ], dbPath: this.database!, }, }); @@ -350,15 +353,19 @@ class DebugController public async expectSucceeded(): Promise { const event = await this.expectCompleted(); - if (event.results.resultType !== QueryResultType.SUCCESS) { - expect(event.results.message).toBe("success"); + const results = Array.from(event.results.results.values()); + expect(results.length).toBe(1); + if (results[0].resultType !== QueryResultType.SUCCESS) { + expect(results[0].message).toBe("success"); } return event; } public async expectFailed(): Promise { const event = await this.expectCompleted(); - expect(event.results.resultType).not.toEqual(QueryResultType.SUCCESS); + const results = Array.from(event.results.results.values()); + expect(results.length).toBe(1); + expect(results[0].resultType).not.toEqual(QueryResultType.SUCCESS); return event; } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debugger.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debugger.test.ts index d92a6a15cf5..0230bf996f0 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debugger.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/debugger/debugger.test.ts @@ -10,10 +10,10 @@ import { import { describeWithCodeQL } from "../../cli"; import { withDebugController } from "./debug-controller"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; -import type { QueryOutputDir } from "../../../../src/local-queries/query-output-dir"; import { createVSCodeCommandManager } from "../../../../src/common/vscode/commands"; import type { AllCommands } from "../../../../src/common/commands"; import { getDataFolderFilePath } from "../utils"; +import type { CoreCompletedQuery } from "../../../../src/query-server"; async function selectForQuickEval( path: string, @@ -30,10 +30,15 @@ async function selectForQuickEval( } async function getResultCount( - outputDir: QueryOutputDir, + completedQuery: CoreCompletedQuery, cli: CodeQLCliServer, ): Promise { - const info = await cli.bqrsInfo(outputDir.bqrsPath, 100); + const results = Array.from(completedQuery.results.values()); + expect(results.length).toBe(1); + const info = await cli.bqrsInfo( + completedQuery.outputDir.getBqrsPath(results[0].outputBaseName), + 100, + ); const resultSet = info["result-sets"][0]; return resultSet.rows; } @@ -104,8 +109,9 @@ describeWithCodeQL()("Debugger", () => { expect(result.started.quickEvalContext!.quickEvalText).toBe( "InterestingNumber", ); - expect(result.results.queryTarget.quickEvalPosition).toBeDefined(); - expect(await getResultCount(result.results.outputDir, cli)).toBe(8); + expect(result.results.queryTargets.length).toBe(1); + expect(result.results.queryTargets[0].quickEvalPosition).toBeDefined(); + expect(await getResultCount(result.results, cli)).toBe(8); await controller.expectStopped(); }); }); @@ -122,8 +128,9 @@ describeWithCodeQL()("Debugger", () => { expect(result.started.quickEvalContext!.quickEvalText).toBe( "InterestingNumber", ); - expect(result.results.queryTarget.quickEvalPosition).toBeDefined(); - expect(await getResultCount(result.results.outputDir, cli)).toBe(0); + expect(result.results.queryTargets.length).toBe(1); + expect(result.results.queryTargets[0].quickEvalPosition).toBeDefined(); + expect(await getResultCount(result.results, cli)).toBe(0); await controller.expectStopped(); }); }); @@ -141,20 +148,15 @@ describeWithCodeQL()("Debugger", () => { expect(result.started.quickEvalContext!.quickEvalText).toBe( "InterestingNumber", ); - expect(result.results.queryTarget.quickEvalPosition).toBeDefined(); - expect(await getResultCount(result.results.outputDir, cli)).toBe(8); + expect(result.results.queryTargets.length).toBe(1); + expect(result.results.queryTargets[0].quickEvalPosition).toBeDefined(); + expect(await getResultCount(result.results, cli)).toBe(8); await controller.expectStopped(); }); }); it("should run a quick evaluation with a bigint-valued result column", async () => { await withDebugController(appCommands, async (controller) => { - const semver = await cli.getVersion(); - if (semver.compare("2.18.4") < 0) { - // Skip this test if the CLI version is too old to support BigInt - return; - } - await selectForQuickEval(quickEvalBigIntQueryPath, 4, 23, 4, 37); // Don't specify a query path, so we'll default to the active document ("QuickEvalBigIntQuery.ql") @@ -165,8 +167,9 @@ describeWithCodeQL()("Debugger", () => { expect(result.started.quickEvalContext!.quickEvalText).toBe( "getBigIntValue", ); - expect(result.results.queryTarget.quickEvalPosition).toBeDefined(); - expect(await getResultCount(result.results.outputDir, cli)).toBe(8); + expect(result.results.queryTargets.length).toBe(1); + expect(result.results.queryTargets[0].quickEvalPosition).toBeDefined(); + expect(await getResultCount(result.results, cli)).toBe(8); await controller.expectStopped(); }); }); @@ -218,7 +221,7 @@ describeWithCodeQL()("Debugger", () => { await controller.expectSessionClosed(); // Expect the number of results to be the same as if we had run the simple query, not the quick eval query. - expect(await getResultCount(result.results.outputDir, cli)).toBe(2); + expect(await getResultCount(result.results, cli)).toBe(2); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index cd5712382be..4e5c9489869 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -78,6 +78,7 @@ describe("SkeletonQueryWizard", () => { getSupportedLanguages: jest .fn() .mockResolvedValue([ + "actions", "ruby", "rust", "javascript", diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts index 8f3f3216827..239b1ca0435 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts @@ -162,7 +162,7 @@ describeWithCodeQL()("Queries", () => { async function runQueryWithExtensions() { console.log("Calling compileAndRunQuery"); - const result = await compileAndRunQuery( + const completedQuery = await compileAndRunQuery( mode, appCommandManager, localQueries, @@ -176,12 +176,14 @@ describeWithCodeQL()("Queries", () => { console.log("Completed compileAndRunQuery"); // Check that query was successful - expect(result.resultType).toBe(QueryResultType.SUCCESS); + const results = Array.from(completedQuery.results.values()); + expect(results.length).toBe(1); + expect(results[0].resultType).toBe(QueryResultType.SUCCESS); console.log("Loading query results"); // Load query results const chunk = await qs.cliServer.bqrsDecode( - result.outputDir.bqrsPath, + completedQuery.outputDir.getBqrsPath(results[0].outputBaseName), SELECT_QUERY_NAME, { // there should only be one result @@ -198,7 +200,7 @@ describeWithCodeQL()("Queries", () => { describe.each(MODES)("running queries (%s)", (mode) => { it("should run a query", async () => { - const result = await compileAndRunQuery( + const completedQuery = await compileAndRunQuery( mode, appCommandManager, localQueries, @@ -211,13 +213,15 @@ describeWithCodeQL()("Queries", () => { ); // just check that the query was successful - expect(result.resultType).toBe(QueryResultType.SUCCESS); + const results = Array.from(completedQuery.results.values()); + expect(results.length).toBe(1); + expect(results[0].resultType).toBe(QueryResultType.SUCCESS); }); // Asserts a fix for bug https://github.com/github/vscode-codeql/issues/733 it("should restart the database and run a query", async () => { await appCommandManager.execute("codeQL.restartQueryServer"); - const result = await compileAndRunQuery( + const completedQuery = await compileAndRunQuery( mode, appCommandManager, localQueries, @@ -229,7 +233,9 @@ describeWithCodeQL()("Queries", () => { undefined, ); - expect(result.resultType).toBe(QueryResultType.SUCCESS); + const results = Array.from(completedQuery.results.values()); + expect(results.length).toBe(1); + expect(results[0].resultType).toBe(QueryResultType.SUCCESS); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/query-server/query-server-client.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/query-server/query-server-client.test.ts index c986e69ae62..3fe7c6fadfb 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/query-server/query-server-client.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/query-server/query-server-client.test.ts @@ -3,9 +3,12 @@ import { dirSync } from "tmp"; import { CancellationTokenSource } from "vscode-jsonrpc"; import type { RunQueryParams } from "../../../../src/query-server/messages"; import { + clearCache, QueryResultType, registerDatabases, runQuery, + trimCache, + trimCacheWithMode, } from "../../../../src/query-server/messages"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { BqrsCellValue } from "../../../../src/common/bqrs-cli-types"; @@ -189,14 +192,67 @@ describeWithCodeQL()("using the query server", () => { it(`should have correct results for query ${queryName}`, async () => { await parsedResults.done(); expect(actualResultSets).not.toEqual({}); - expect(Object.keys(actualResultSets!).sort()).toEqual( + expect(Object.keys(actualResultSets).sort()).toEqual( Object.keys(queryTestCase.expectedResultSets).sort(), ); for (const name in queryTestCase.expectedResultSets) { - expect(actualResultSets![name]).toEqual( + expect(actualResultSets[name]).toEqual( queryTestCase.expectedResultSets[name], ); } }); + + it("should invoke codeQL.trimOverlayBaseCache command when queryServerTrimCacheWithMode is enabled", async () => { + const features = (await cliServer.getFeatures()) as { + [feature: string]: boolean | undefined; + }; + + // Register the database first (if not already done) + await qs.sendRequest(registerDatabases, { databases: [db] }); + + try { + // Send the trimCacheWithMode request + const params = { + db, + mode: "overlay", + }; + const result = await qs.sendRequest( + trimCacheWithMode, + params, + token, + () => {}, + ); + + // The result should contain a deletionMessage string + expect(result).toHaveProperty("deletionMessage"); + expect(typeof result.deletionMessage).toBe("string"); + expect(features.queryServerTrimCacheWithMode).toBeTruthy(); + } catch (e) { + expect(features.queryServerTrimCacheWithMode).toBeFalsy(); + expect((e as Error).message).toContain( + "Unsupported request method: evaluation/trimCacheWithMode", + ); + } + }); + + it("should invoke trimCache command and receive a deletionMessage", async () => { + // Register the database first (if not already done) + await qs.sendRequest(registerDatabases, { databases: [db] }); + + const params = { db }; + const result = await qs.sendRequest(trimCache, params, token, () => {}); + expect(result).toHaveProperty("deletionMessage"); + expect(typeof result.deletionMessage).toBe("string"); + }); + + it("should invoke clearCache command and receive a deletionMessage", async () => { + // Register the database first (if not already done) + await qs.sendRequest(registerDatabases, { databases: [db] }); + + const params = { db, dryRun: false }; + const result = await qs.sendRequest(clearCache, params, token, () => {}); + expect(result).toHaveProperty("deletionMessage"); + expect(typeof result.deletionMessage).toBe("string"); + }); } }); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/run-cli.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/run-cli.test.ts index ef92ee39e06..326cfdf4892 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/run-cli.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/run-cli.test.ts @@ -16,7 +16,7 @@ import { faker } from "@faker-js/faker"; import { getActivatedExtension } from "../global.helper"; import type { BaseLogger } from "../../../src/common/logging"; import { getQlPackForDbscheme } from "../../../src/databases/qlpack"; -import { dbSchemeToLanguage } from "../../../src/common/query-language"; +import { languageToDbScheme } from "../../../src/common/query-language"; /** * Perform proper integration tests by running the CLI @@ -27,14 +27,6 @@ describe("Use cli", () => { let logSpy: jest.SpiedFunction; - const languageToDbScheme = Object.entries(dbSchemeToLanguage).reduce( - (acc, [k, v]) => { - acc[v] = k; - return acc; - }, - {} as { [k: string]: string }, - ); - beforeEach(async () => { const extension = await getActivatedExtension(); cli = extension.cliServer; @@ -107,12 +99,17 @@ describe("Use cli", () => { itWithCodeQL()( "should resolve printAST queries for supported languages", async () => { - for (const lang of supportedLanguages) { + for (let lang of supportedLanguages) { if (lang === "go") { // The codeql-go submodule is not available in the integration tests. return; } + if (lang === "actions") { + // The actions queries use the javascript dbscheme. + lang = "javascript"; + } + console.log(`resolving printAST queries for ${lang}`); const pack = await getQlPackForDbscheme(cli, languageToDbScheme[lang]); expect(pack.dbschemePack).toContain(lang); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-submission-integration.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-submission-integration.test.ts index 182433637a7..928371856a8 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-submission-integration.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-submission-integration.test.ts @@ -13,7 +13,7 @@ async function showQlDocument(name: string): Promise { const folderPath = workspace.workspaceFolders![0].uri.fsPath; const documentPath = resolve(folderPath, name); const document = await workspace.openTextDocument(documentPath); - await window.showTextDocument(document!); + await window.showTextDocument(document); return document; } diff --git a/extensions/ql-vscode/test/vscode-tests/ensureCli.ts b/extensions/ql-vscode/test/vscode-tests/ensureCli.ts index 4ddad1afbc6..4c090a10910 100644 --- a/extensions/ql-vscode/test/vscode-tests/ensureCli.ts +++ b/extensions/ql-vscode/test/vscode-tests/ensureCli.ts @@ -119,7 +119,6 @@ async function downloadWithProgress(url: string, filePath: string) { let numBytesDownloaded = 0; let lastMessage = 0; - // eslint-disable-next-line no-constant-condition -- This is a loop that reads from a stream while (true) { const { done, value } = await body.read(); if (done) { diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/determining-selected-query-test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/determining-selected-query-test.ts index f05786680b6..c6c07c161da 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/determining-selected-query-test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/determining-selected-query-test.ts @@ -10,7 +10,7 @@ async function showQlDocument(name: string): Promise { const folderPath = workspace.workspaceFolders![0].uri.fsPath; const documentPath = resolve(folderPath, name); const document = await workspace.openTextDocument(documentPath); - await window.showTextDocument(document!); + await window.showTextDocument(document); return document; } diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts index 4634cac823c..aa86d1a9ed1 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts @@ -429,7 +429,7 @@ describe("Distribution updates", () => { async () => { const file = await readFile(codeqlReleaseZipPath); - return HttpResponse.arrayBuffer(file, { + return HttpResponse.arrayBuffer(file.buffer, { headers: { "Content-Type": "application/octet-stream", }, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts index fd033de5f06..6275abd6b7b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts @@ -2,7 +2,6 @@ import { readFileSync } from "fs-extra"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { Uri } from "vscode"; -import { QueryOutputDir } from "../../../../../src/local-queries/query-output-dir"; import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers"; import path from "path"; import { AstBuilder } from "../../../../../src/language-support"; @@ -141,7 +140,7 @@ describe("AstBuilder", () => { function createAstBuilder() { return new AstBuilder( - new QueryOutputDir("/a/b/c"), + path.normalize("/a/b/c/results.bqrs"), mockCli, mockDatabaseItem({ resolveSourceFile: undefined, @@ -160,7 +159,7 @@ describe("AstBuilder", () => { edges: 1, graphProperties: 2, }; - const index = mapper[resultSet] as number; + const index = mapper[resultSet]; if (index >= 0 && index <= 2) { return JSON.parse( readFileSync( diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/external-api-usage-query.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/external-api-usage-query.test.ts index 7a4fcd6f531..bdc407502cb 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/external-api-usage-query.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/external-api-usage-query.test.ts @@ -30,15 +30,13 @@ describe("runModelEditorQueries", () => { > = jest.spyOn(log, "showAndLogExceptionWithTelemetry"); const outputDir = new QueryOutputDir(join((await file()).path, "1")); - + const queryPath = "/a/b/c/ApplicationModeEndpoints.ql"; const options = { cliServer: mockedObject({ resolveQlpacks: jest.fn().mockResolvedValue({ "my/extensions": "/a/b/c/", }), - resolveQueriesInSuite: jest - .fn() - .mockResolvedValue(["/a/b/c/ApplicationModeEndpoints.ql"]), + resolveQueriesInSuite: jest.fn().mockResolvedValue([queryPath]), packPacklist: jest .fn() .mockResolvedValue([ @@ -50,7 +48,9 @@ describe("runModelEditorQueries", () => { queryRunner: mockedObject({ createQueryRun: jest.fn().mockReturnValue({ evaluate: jest.fn().mockResolvedValue({ - resultType: QueryResultType.CANCELLATION, + results: new Map([ + [queryPath, { resultType: QueryResultType.CANCELLATION }], + ]), }), outputDir, }), @@ -88,15 +88,13 @@ describe("runModelEditorQueries", () => { it("should run query for random language", async () => { const outputDir = new QueryOutputDir(join((await file()).path, "1")); - + const queryPath = "/a/b/c/ApplicationModeEndpoints.ql"; const options = { cliServer: mockedObject({ resolveQlpacks: jest.fn().mockResolvedValue({ "my/extensions": "/a/b/c/", }), - resolveQueriesInSuite: jest - .fn() - .mockResolvedValue(["/a/b/c/ApplicationModeEndpoints.ql"]), + resolveQueriesInSuite: jest.fn().mockResolvedValue([queryPath]), packPacklist: jest .fn() .mockResolvedValue([ @@ -122,6 +120,9 @@ describe("runModelEditorQueries", () => { createQueryRun: jest.fn().mockReturnValue({ evaluate: jest.fn().mockResolvedValue({ resultType: QueryResultType.SUCCESS, + results: new Map([ + [queryPath, { resultType: QueryResultType.SUCCESS }], + ]), outputDir, }), outputDir, @@ -156,17 +157,20 @@ describe("runModelEditorQueries", () => { expect(options.cliServer.resolveQlpacks).toHaveBeenCalledWith([], true); expect(options.queryRunner.createQueryRun).toHaveBeenCalledWith( "/a/b/c/src.zip", - { - queryPath: expect.stringMatching(/\S*ModeEndpoints\.ql/), - quickEvalPosition: undefined, - quickEvalCountOnly: false, - }, + [ + { + outputBaseName: "results", + queryPath: expect.stringMatching(/\S*ModeEndpoints\.ql/), + quickEvalPosition: undefined, + quickEvalCountOnly: false, + }, + ], false, [], ["my/extensions"], {}, "/tmp/queries", - undefined, + "ApplicationModeEndpoints.ql", undefined, ); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts index 173f901a7ee..7686e66d912 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts @@ -28,12 +28,10 @@ describe("runGenerateQueries", () => { const outputDir = new QueryOutputDir(join(queryStorageDir, "1")); const onResults = jest.fn(); - + const queryPath = "/a/b/c/GenerateModel.ql"; const options = { cliServer: mockedObject({ - resolveQueriesInSuite: jest - .fn() - .mockResolvedValue(["/a/b/c/GenerateModel.ql"]), + resolveQueriesInSuite: jest.fn().mockResolvedValue([queryPath]), bqrsDecodeAll: jest.fn().mockResolvedValue({ sourceModel: { columns: [ @@ -101,7 +99,9 @@ describe("runGenerateQueries", () => { queryRunner: mockedObject({ createQueryRun: jest.fn().mockReturnValue({ evaluate: jest.fn().mockResolvedValue({ - resultType: QueryResultType.SUCCESS, + results: new Map([ + [queryPath, { resultType: QueryResultType.SUCCESS }], + ]), outputDir, }), outputDir, @@ -221,17 +221,20 @@ describe("runGenerateQueries", () => { expect(options.queryRunner.createQueryRun).toHaveBeenCalledTimes(1); expect(options.queryRunner.createQueryRun).toHaveBeenCalledWith( "/a/b/c/src.zip", - { - queryPath: "/a/b/c/GenerateModel.ql", - quickEvalPosition: undefined, - quickEvalCountOnly: false, - }, + [ + { + outputBaseName: "results", + queryPath: "/a/b/c/GenerateModel.ql", + quickEvalPosition: undefined, + quickEvalCountOnly: false, + }, + ], false, [], undefined, {}, "/tmp/queries", - undefined, + "GenerateModel.ql", undefined, ); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts index b049aa672fa..c5936e3ec1c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts @@ -402,11 +402,7 @@ describe("MethodsUsageDataProvider", () => { modifiedMethodSignatures, ); expect( - dataProvider - .getChildren() - .map( - (item) => (item as MethodsUsageTreeViewItem).method.signature, - ), + dataProvider.getChildren().map((item) => item.method.signature), ).toEqual(["b.a.C.a()", "b.a.C.b()", "b.a.C.d()", "a.b.C.d()"]); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/suggestion-queries.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/suggestion-queries.test.ts index e5c71658717..eeb41f869e1 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/suggestion-queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/suggestion-queries.test.ts @@ -146,9 +146,8 @@ describe("runSuggestionsQuery", () => { .mockResolvedValueOnce(mockInputSuggestions) .mockResolvedValueOnce(mockOutputSuggestions); - const resolveQueriesInSuite = jest - .fn() - .mockResolvedValue(["/a/b/c/FrameworkModeAccessPathSuggestions.ql"]); + const queryPath = "/a/b/c/FrameworkModeAccessPathSuggestions.ql"; + const resolveQueriesInSuite = jest.fn().mockResolvedValue([queryPath]); const options = { parseResults, @@ -173,7 +172,9 @@ describe("runSuggestionsQuery", () => { queryRunner: mockedObject({ createQueryRun: jest.fn().mockReturnValue({ evaluate: jest.fn().mockResolvedValue({ - resultType: QueryResultType.SUCCESS, + results: new Map([ + [queryPath, { resultType: QueryResultType.SUCCESS }], + ]), outputDir, }), outputDir, @@ -206,17 +207,20 @@ describe("runSuggestionsQuery", () => { expect(options.cliServer.resolveQlpacks).toHaveBeenCalledWith([], true); expect(options.queryRunner.createQueryRun).toHaveBeenCalledWith( "/a/b/c/src.zip", - { - queryPath: expect.stringMatching(/\S*AccessPathSuggestions\.ql/), - quickEvalPosition: undefined, - quickEvalCountOnly: false, - }, + [ + { + queryPath: expect.stringMatching(/\S*AccessPathSuggestions\.ql/), + outputBaseName: "results", + quickEvalPosition: undefined, + quickEvalCountOnly: false, + }, + ], false, [], ["my/extensions"], {}, "/tmp/queries", - undefined, + "FrameworkModeAccessPathSuggestions.ql", undefined, ); expect(options.cliServer.resolveQueriesInSuite).toHaveBeenCalledTimes(1); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts index 34c092987cc..e8ae5a82975 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts @@ -237,12 +237,12 @@ describe("write and read", () => { dbPath = "/a/b/c", ): QueryWithResults { // pretend that the results path exists - const resultsPath = join(queryPath, "results.bqrs"); mkdirpSync(queryPath); - writeFileSync(resultsPath, "", "utf8"); + writeFileSync(join(queryPath, "results.bqrs"), "", "utf8"); const queryEvalInfo = new QueryEvaluationInfo( queryPath, + "results", Uri.file(dbPath).fsPath, true, undefined, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index a2bc725849e..fc31fc909e3 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -127,7 +127,7 @@ describe("query-results", () => { const expectedResultsPath = join(queryPath, "results.bqrs"); const expectedSortedResultsPath = join( queryPath, - "sortedResults-cc8589f226adc134f87f2438e10075e0667571c72342068e2281e0b3b65e1092.bqrs", + "results-sorted-cc8589f226adc134f87f2438e10075e0667571c72342068e2281e0b3b65e1092.bqrs", ); expect(spy).toHaveBeenCalledWith( expectedResultsPath, @@ -419,12 +419,12 @@ describe("query-results", () => { dbPath = "/a/b/c", ): QueryWithResults { // pretend that the results path exists - const resultsPath = join(queryPath, "results.bqrs"); mkdirpSync(queryPath); - writeFileSync(resultsPath, "", "utf8"); + writeFileSync(join(queryPath, "results.bqrs"), "", "utf8"); const queryEvalInfo = new QueryEvaluationInfo( queryPath, + "results", Uri.file(dbPath).fsPath, true, undefined, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts index 45e0063b1bb..8437249e815 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/run-queries.test.ts @@ -28,12 +28,10 @@ describe("run-queries", () => { const saveDir = "query-save-dir"; const queryEvalInfo = createMockQueryEvaluationInfo(true, saveDir); - expect(queryEvalInfo.dilPath).toBe(join(saveDir, "results.dil")); - expect(queryEvalInfo.resultsPaths.resultsPath).toBe( - join(saveDir, "results.bqrs"), - ); - expect(queryEvalInfo.resultsPaths.interpretedResultsPath).toBe( - join(saveDir, "interpretedResults.sarif"), + expect(queryEvalInfo.dilPath).toBe(join(saveDir, "foo.dil")); + expect(queryEvalInfo.resultsPath).toBe(join(saveDir, "foo.bqrs")); + expect(queryEvalInfo.interpretedResultsPath).toBe( + join(saveDir, "foo-interpreted.sarif"), ); expect(queryEvalInfo.dbItemPath).toBe(Uri.file("/abc").fsPath); }); @@ -215,6 +213,7 @@ describe("run-queries", () => { ) { return new QueryEvaluationInfo( saveDir, + "foo", Uri.parse("file:///abc").fsPath, databaseHasMetadataFile, undefined, diff --git a/extensions/ql-vscode/test/vscode-tests/test-dispose-handler.ts b/extensions/ql-vscode/test/vscode-tests/test-dispose-handler.ts index 94f490dafe1..1ecc6c7aa83 100644 --- a/extensions/ql-vscode/test/vscode-tests/test-dispose-handler.ts +++ b/extensions/ql-vscode/test/vscode-tests/test-dispose-handler.ts @@ -1,13 +1,19 @@ -import type { Disposable } from "vscode"; +import type { Disposable, TreeView } from "vscode"; import { DisposableObject } from "../../src/common/disposable-object"; -export function testDisposeHandler(disposable: any & Disposable) { - if ( - disposable.onDidExpandElement && - disposable.onDidCollapseElement && - disposable.reveal - ) { - // This looks like a treeViewer. Don't dispose +function isTreeView(obj: unknown): obj is TreeView { + return ( + typeof obj === "object" && + obj !== null && + "onDidExpandElement" in obj && + "onDidCollapseElement" in obj && + "reveal" in obj + ); +} + +export function testDisposeHandler(disposable: Disposable) { + if (isTreeView(disposable)) { + // This looks like a TreeView. Don't dispose return; } if (disposable instanceof DisposableObject) {