From b0d673eb803c2421bf7c8c5128c8f13b4e8e434e Mon Sep 17 00:00:00 2001 From: Naoto Ono Date: Mon, 22 Sep 2025 19:34:06 +0900 Subject: [PATCH] WIP: Initial API design --- .ruby-version | 1 + .vscodeignore | 2 + README.md | 22 + activation.rb | 7 + chruby_activation.rb | 27 + eslint.config.mjs | 6 + package.json | 17 +- rubyEnvironmentsApi/.npmignore | 3 + rubyEnvironmentsApi/LICENSE.txt | 21 + rubyEnvironmentsApi/README.md | 29 + rubyEnvironmentsApi/eslint.config.mjs | 50 + rubyEnvironmentsApi/package.json | 31 + rubyEnvironmentsApi/src/index.ts | 92 + rubyEnvironmentsApi/tsconfig.json | 13 + rubyEnvironmentsApi/yarn.lock | 3198 +++++++++++++++++++++ src/common.ts | 15 + src/extension.ts | 124 +- src/ruby.ts | 445 +++ src/ruby/asdf.ts | 88 + src/ruby/chruby.ts | 440 +++ src/ruby/custom.ts | 35 + src/ruby/mise.ts | 65 + src/ruby/none.ts | 39 + src/ruby/rbenv.ts | 42 + src/ruby/rubyInstaller.ts | 65 + src/ruby/rvm.ts | 50 + src/ruby/shadowenv.ts | 72 + src/ruby/versionManager.ts | 111 + src/test/extension.test.ts | 7 - src/test/rubyVersion.ts | 9 + src/test/runTest.ts | 25 + src/test/suite/fakeTelemetry.ts | 55 + src/test/suite/helpers.ts | 72 + src/test/suite/index.ts | 44 + src/test/suite/ruby.test.ts | 160 ++ src/test/suite/ruby/asdf.test.ts | 167 ++ src/test/suite/ruby/chruby.test.ts | 275 ++ src/test/suite/ruby/custom.test.ts | 73 + src/test/suite/ruby/mise.test.ts | 130 + src/test/suite/ruby/none.test.ts | 69 + src/test/suite/ruby/rbenv.test.ts | 121 + src/test/suite/ruby/rubyInstaller.test.ts | 118 + src/test/suite/ruby/rvm.test.ts | 79 + src/test/suite/ruby/shadowenv.test.ts | 196 ++ src/test/suite/workspaceChannel.test.ts | 24 + src/workspaceChannel.ts | 72 + tsconfig.json | 3 +- yarn.lock | 59 +- 48 files changed, 6846 insertions(+), 22 deletions(-) create mode 100644 .ruby-version create mode 100644 activation.rb create mode 100644 chruby_activation.rb create mode 100644 rubyEnvironmentsApi/.npmignore create mode 100644 rubyEnvironmentsApi/LICENSE.txt create mode 100644 rubyEnvironmentsApi/README.md create mode 100644 rubyEnvironmentsApi/eslint.config.mjs create mode 100644 rubyEnvironmentsApi/package.json create mode 100644 rubyEnvironmentsApi/src/index.ts create mode 100644 rubyEnvironmentsApi/tsconfig.json create mode 100644 rubyEnvironmentsApi/yarn.lock create mode 100644 src/common.ts create mode 100644 src/ruby.ts create mode 100644 src/ruby/asdf.ts create mode 100644 src/ruby/chruby.ts create mode 100644 src/ruby/custom.ts create mode 100644 src/ruby/mise.ts create mode 100644 src/ruby/none.ts create mode 100644 src/ruby/rbenv.ts create mode 100644 src/ruby/rubyInstaller.ts create mode 100644 src/ruby/rvm.ts create mode 100644 src/ruby/shadowenv.ts create mode 100644 src/ruby/versionManager.ts delete mode 100644 src/test/extension.test.ts create mode 100644 src/test/rubyVersion.ts create mode 100644 src/test/runTest.ts create mode 100644 src/test/suite/fakeTelemetry.ts create mode 100644 src/test/suite/helpers.ts create mode 100644 src/test/suite/index.ts create mode 100644 src/test/suite/ruby.test.ts create mode 100644 src/test/suite/ruby/asdf.test.ts create mode 100644 src/test/suite/ruby/chruby.test.ts create mode 100644 src/test/suite/ruby/custom.test.ts create mode 100644 src/test/suite/ruby/mise.test.ts create mode 100644 src/test/suite/ruby/none.test.ts create mode 100644 src/test/suite/ruby/rbenv.test.ts create mode 100644 src/test/suite/ruby/rubyInstaller.test.ts create mode 100644 src/test/suite/ruby/rvm.test.ts create mode 100644 src/test/suite/ruby/shadowenv.test.ts create mode 100644 src/test/suite/workspaceChannel.test.ts create mode 100644 src/workspaceChannel.ts diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..4f5e697 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.4.5 diff --git a/.vscodeignore b/.vscodeignore index 159277f..f9f2938 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -12,3 +12,5 @@ vsc-extension-quickstart.md **/*.map **/*.ts **/.vscode-test.* + +rubyEnvironmentsApi/** diff --git a/README.md b/README.md index 41955e3..bd5fd2d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,28 @@ TODO: this extension is getting extracted from the Ruby LSP. TODO +``` +{ + "extensionDependencies": [ + "shopify.ruby-environments" + ], + "dependencies": { + "@shopify/ruby-environments": "..." + }, +} +``` + ## API TODO + +``` +// Import the API +import { RubyEnvironments } from '@shopify/ruby-environments'; + +// Load the RubyEnvironments API +const rubyEnvApi: RubyEnvironmentsApi = await RubyEnvironmentsApi.getApi(); + +// Retrieve the resolved Ruby environment. +const rubyEnv = await rubyEnvApi.getEnvironment(vscode.workspace.workspaceFolders[0]); +``` diff --git a/activation.rb b/activation.rb new file mode 100644 index 0000000..dfc33dd --- /dev/null +++ b/activation.rb @@ -0,0 +1,7 @@ +# Using .map.compact just so that it doesn't crash immediately on Ruby 2.6 +env = ENV.map do |k, v| + utf_8_value = v.dup.force_encoding(Encoding::UTF_8) + "#{k}RUBY_LSP_VS#{utf_8_value}" if utf_8_value.valid_encoding? +end.compact +env.unshift(RUBY_VERSION, Gem.path.join(","), !!defined?(RubyVM::YJIT)) +STDERR.print("RUBY_LSP_ACTIVATION_SEPARATOR#{env.join("RUBY_LSP_FS")}RUBY_LSP_ACTIVATION_SEPARATOR") diff --git a/chruby_activation.rb b/chruby_activation.rb new file mode 100644 index 0000000..35e082d --- /dev/null +++ b/chruby_activation.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Typically, GEM_HOME points to $HOME/.gem/ruby/version_without_patch. For example, for Ruby 3.2.2, it would be +# $HOME/.gem/ruby/3.2.0. However, chruby overrides GEM_HOME to use the patch part of the version, resulting in +# $HOME/.gem/ruby/3.2.2. In our activation script, we check if a directory using the patch exists and then prefer +# that over the default one. +user_dir = Gem.user_dir +paths = Gem.path +default_dir = Gem.default_dir + +if paths.length > 2 + paths.delete(default_dir) + paths.delete(user_dir) + first_path = paths[0] + user_dir = first_path if first_path && Dir.exist?(first_path) +end + +newer_gem_home = File.join(File.dirname(user_dir), ARGV.first) +gems = Dir.exist?(newer_gem_home) ? newer_gem_home : user_dir +STDERR.print( + [ + default_dir, + gems, + !!defined?(RubyVM::YJIT), + RUBY_VERSION + ].join("RUBY_LSP_ACTIVATION_SEPARATOR") +) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9ddc125..2c1c52e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -32,10 +32,16 @@ export default tseslint.config( "**/dist/", ".vscode-test.mjs", "esbuild.js", + "rubyEnvironmentsApi" ], }, { rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unused-vars": [ "error", { diff --git a/package.json b/package.json index 15f57fd..8b247fc 100644 --- a/package.json +++ b/package.json @@ -36,20 +36,25 @@ "test": "vscode-test" }, "devDependencies": { + "@eslint/js": "^9.32.0", "@types/mocha": "^10.0.10", "@types/node": "20.x", + "@types/sinon": "^17.0.4", "@types/vscode": "^1.102.0", - "@eslint/js": "^9.32.0", - "eslint": "^9.30.1", - "typescript-eslint": "^8.38.0", - "eslint-plugin-prettier": "^5.5.3", - "prettier": "^3.6.2", "@vscode/test-cli": "^0.0.11", "@vscode/test-electron": "^2.5.2", "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.3", + "eslint": "^9.30.1", + "eslint-plugin-prettier": "^5.5.3", "npm-run-all": "^4.1.5", "ovsx": "^0.10.5", - "typescript": "^5.8.3" + "prettier": "^3.6.2", + "sinon": "^21.0.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0" + }, + "dependencies": { + "glob": "^11.0.3" } } diff --git a/rubyEnvironmentsApi/.npmignore b/rubyEnvironmentsApi/.npmignore new file mode 100644 index 0000000..8e1cbc8 --- /dev/null +++ b/rubyEnvironmentsApi/.npmignore @@ -0,0 +1,3 @@ +node_modules +src +tsconfig.json diff --git a/rubyEnvironmentsApi/LICENSE.txt b/rubyEnvironmentsApi/LICENSE.txt new file mode 100644 index 0000000..c3ef662 --- /dev/null +++ b/rubyEnvironmentsApi/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022-present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/rubyEnvironmentsApi/README.md b/rubyEnvironmentsApi/README.md new file mode 100644 index 0000000..3f74fe1 --- /dev/null +++ b/rubyEnvironmentsApi/README.md @@ -0,0 +1,29 @@ +# Ruby environments + +This npm module provides a TypeScript API for interacting with Ruby environments VS Code extension. + +## Extension Settings + +``` +{ + "extensionDependencies": [ + "shopify.ruby-environments" + ], + "dependencies": { + "@shopify/ruby-environments": "..." + }, +} +``` + +## API + +``` +// Import the API +import { RubyEnvironments } from '@shopify/ruby-environments'; + +// Load the RubyEnvironments API +const rubyEnvApi: RubyEnvironmentsApi = await RubyEnvironmentsApi.getApi(); + +// Retrieve the resolved Ruby environment. +const rubyEnv = await rubyEnvApi.getEnvironment(vscode.workspace.workspaceFolders[0]); +``` diff --git a/rubyEnvironmentsApi/eslint.config.mjs b/rubyEnvironmentsApi/eslint.config.mjs new file mode 100644 index 0000000..b072ee2 --- /dev/null +++ b/rubyEnvironmentsApi/eslint.config.mjs @@ -0,0 +1,50 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from "typescript-eslint"; +import eslintPluginPrettier from "eslint-plugin-prettier"; + +export default tseslint.config( + eslint.configs.recommended, + tseslint.configs.recommended, + tseslint.configs.recommendedTypeChecked, + { + plugins: { + prettier: eslintPluginPrettier, + }, + rules: { + "prettier/prettier": "warn", + }, + }, + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + ignores: [ + "eslint.config.mjs", + "**/out/", + ], + }, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, + ], + "no-console": "warn", + "no-template-curly-in-string": "warn", + "eqeqeq": "error", + "no-invalid-this": "error", + "no-lonely-if": "error" + } + } +); diff --git a/rubyEnvironmentsApi/package.json b/rubyEnvironmentsApi/package.json new file mode 100644 index 0000000..ca305e3 --- /dev/null +++ b/rubyEnvironmentsApi/package.json @@ -0,0 +1,31 @@ +{ + "name": "@shopify/ruby-environments", + "version": "0.0.1", + "description": "API for the VS Code Ruby environments extension", + "main": "./out/index.js", + "types": "./out/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/Shopify/ruby-environments.git" + }, + "author": "Shopify", + "license": "MIT", + "scripts": { + "compile": "tsc -p ./", + "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", + "check-types": "tsc --noEmit", + "format": "eslint . --fix && prettier '**/*.{ts,json,md,yaml,yml}' --write", + "lint": "eslint . && prettier '**/*.{ts,json,md,yaml,yml}' --check" + }, + "devDependencies": { + "@types/node": "20.x", + "@types/vscode": "^1.102.0", + "@eslint/js": "^9.32.0", + "eslint": "^9.30.1", + "typescript-eslint": "^8.38.0", + "eslint-plugin-prettier": "^5.5.3", + "prettier": "^3.6.2", + "esbuild": "^0.25.3", + "typescript": "^5.8.3" + } +} diff --git a/rubyEnvironmentsApi/src/index.ts b/rubyEnvironmentsApi/src/index.ts new file mode 100644 index 0000000..114b19c --- /dev/null +++ b/rubyEnvironmentsApi/src/index.ts @@ -0,0 +1,92 @@ +import { WorkspaceFolder, extensions } from "vscode"; + +/** + * Identifiers for supported Ruby version managers. + */ +export enum ManagerIdentifier { + Asdf = "asdf", + Auto = "auto", + Chruby = "chruby", + Rbenv = "rbenv", + Rvm = "rvm", + Shadowenv = "shadowenv", + Mise = "mise", + RubyInstaller = "rubyInstaller", + None = "none", + Custom = "custom", +} + +/** + * Configuration for a Ruby version manager. + * @property identifier The type of version manager in use. + */ +export interface ManagerConfiguration { + /** The type of version manager in use. */ + identifier: ManagerIdentifier; +} + +/** + * Represents a fully resolved Ruby environment for a workspace. + * @property env The environment variables for the Ruby process. + * @property rubyVersion The active Ruby version (optional). + * @property yjitEnabled Whether YJIT is enabled (optional). + * @property gemPath Array of gem paths for the environment. + * @property versionManager The configuration of the version manager used. + */ +export interface RubyEnvironment { + /** The environment variables for the Ruby process. */ + env: NodeJS.ProcessEnv; + /** The active Ruby version, if detected. */ + rubyVersion?: string; + /** Whether YJIT is enabled, if detected. */ + yjitEnabled?: boolean; + /** Array of gem paths for the environment. */ + gemPath: string[]; + /** The configuration of the version manager used. */ + versionManager: ManagerConfiguration; +} + +/** + * Main API for interacting with Ruby environments in VS Code extensions. + */ +export interface RubyEnvironmentsApi { + /** + * Get the resolved Ruby environment for a workspace folder. + * @param workspaceFolder The workspace folder to resolve the environment for. + */ + getEnvironment(workspaceFolder: WorkspaceFolder): Promise; + + /** + * Update the Ruby environment for a workspace folder. + * @param env The environment variables to update. + * @param workspaceFolder The workspace folder to update. + */ + updateEnvironment(env: NodeJS.ProcessEnv, workspaceFolder: WorkspaceFolder): Promise; + + /** + * Force the Ruby environment extension to re-resolve the environment for a workspace folder. + * @param workspaceFolder The workspace folder to refresh. + */ + refreshEnvironment(workspaceFolder: WorkspaceFolder): Promise; +} + +/** + * The extension identifier for the Ruby environments API. + */ +const EXTENSION_NAME = "shopify.ruby-environments"; + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace RubyEnvironmentsApi { + /** + * Return the API exported by the Ruby Environments extension. + * @throws If the extension is not found or cannot be activated. + */ + export async function getApi(): Promise { + const extension = extensions.getExtension(EXTENSION_NAME); + if (!extension) { + throw new Error(`Extension ${EXTENSION_NAME} not found`); + } + await extension.activate(); + return extension.exports as RubyEnvironmentsApi; + } +} diff --git a/rubyEnvironmentsApi/tsconfig.json b/rubyEnvironmentsApi/tsconfig.json new file mode 100644 index 0000000..355fe22 --- /dev/null +++ b/rubyEnvironmentsApi/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "lib": ["ES2022"], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */, + "outDir": "out" /* Specify an output folder for all emitted files. */, + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */ + } +} diff --git a/rubyEnvironmentsApi/yarn.lock b/rubyEnvironmentsApi/yarn.lock new file mode 100644 index 0000000..2c08950 --- /dev/null +++ b/rubyEnvironmentsApi/yarn.lock @@ -0,0 +1,3198 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@azu/format-text@^1.0.1", "@azu/format-text@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@azu/format-text/-/format-text-1.0.2.tgz#abd46dab2422e312bd1bfe36f0d427ab6039825d" + integrity sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg== + +"@azu/style-format@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@azu/style-format/-/style-format-1.0.1.tgz#b3643af0c5fee9d53e69a97c835c404bdc80f792" + integrity sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g== + dependencies: + "@azu/format-text" "^1.0.1" + +"@azure/abort-controller@^2.0.0", "@azure/abort-controller@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" + integrity sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA== + dependencies: + tslib "^2.6.2" + +"@azure/core-auth@^1.10.0", "@azure/core-auth@^1.9.0": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.10.1.tgz#68a17fa861ebd14f6fd314055798355ef6bedf1b" + integrity sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-util" "^1.13.0" + tslib "^2.6.2" + +"@azure/core-client@^1.9.2": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.10.1.tgz#83d78f97d647ab22e6811a7a68bb4223e7a1d019" + integrity sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.10.0" + "@azure/core-rest-pipeline" "^1.22.0" + "@azure/core-tracing" "^1.3.0" + "@azure/core-util" "^1.13.0" + "@azure/logger" "^1.3.0" + tslib "^2.6.2" + +"@azure/core-rest-pipeline@^1.17.0", "@azure/core-rest-pipeline@^1.22.0": + version "1.22.1" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.1.tgz#f47bc02ff9a79f62e6a32aa375420b1b86dcbccd" + integrity sha512-UVZlVLfLyz6g3Hy7GNDpooMQonUygH7ghdiSASOOHy97fKj/mPLqgDX7aidOijn+sCMU+WU8NjlPlNTgnvbcGA== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@azure/core-auth" "^1.10.0" + "@azure/core-tracing" "^1.3.0" + "@azure/core-util" "^1.13.0" + "@azure/logger" "^1.3.0" + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.3.1.tgz#e971045c901ea9c110616b0e1db272507781d5f6" + integrity sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ== + dependencies: + tslib "^2.6.2" + +"@azure/core-util@^1.11.0", "@azure/core-util@^1.13.0": + version "1.13.1" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.13.1.tgz#6dff2ff6d3c9c6430c6f4d3b3e65de531f10bafe" + integrity sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A== + dependencies: + "@azure/abort-controller" "^2.1.2" + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/identity@^4.1.0": + version "4.12.0" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.12.0.tgz#272e39a8742191aac0b9f5ec683984bf4d88e7cb" + integrity sha512-6vuh2R3Cte6SD6azNalLCjIDoryGdcvDVEV7IDRPtm5lHX5ffkDlIalaoOp5YJU08e4ipjJENel20kSMDLAcug== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.9.0" + "@azure/core-client" "^1.9.2" + "@azure/core-rest-pipeline" "^1.17.0" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.11.0" + "@azure/logger" "^1.0.0" + "@azure/msal-browser" "^4.2.0" + "@azure/msal-node" "^3.5.0" + open "^10.1.0" + tslib "^2.2.0" + +"@azure/logger@^1.0.0", "@azure/logger@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.3.0.tgz#5501cf85d4f52630602a8cc75df76568c969a827" + integrity sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA== + dependencies: + "@typespec/ts-http-runtime" "^0.3.0" + tslib "^2.6.2" + +"@azure/msal-browser@^4.2.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-4.23.0.tgz#9f4e943f2f624f1569acdcde83db7b7781d768d2" + integrity sha512-uHnfRwGAEHaYVXzpCtYsruy6PQxL2v76+MJ3+n/c/3PaTiTIa5ch7VofTUNoA39nHyjJbdiqTwFZK40OOTOkjw== + dependencies: + "@azure/msal-common" "15.12.0" + +"@azure/msal-common@15.12.0": + version "15.12.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-15.12.0.tgz#5c30a05b4396e2dbbc35d27bd9bb636d44ccb00e" + integrity sha512-4ucXbjVw8KJ5QBgnGJUeA07c8iznwlk5ioHIhI4ASXcXgcf2yRFhWzYOyWg/cI49LC9ekpFJeQtO3zjDTbl6TQ== + +"@azure/msal-node@^3.5.0": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-3.7.4.tgz#bbc0f6009e7fcb9916430193a692961b5bbc92be" + integrity sha512-fjqvhrThwzzPvqhFOdkkGRJCHPQZTNijpceVy8QjcfQuH482tOVEjHyamZaioOhVtx+FK1u+eMpJA2Zz4U9LVg== + dependencies: + "@azure/msal-common" "15.12.0" + jsonwebtoken "^9.0.0" + uuid "^8.3.0" + +"@babel/code-frame@^7.26.2": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + +"@emnapi/core@^1.4.3": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.5.0.tgz#85cd84537ec989cebb2343606a1ee663ce4edaf0" + integrity sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg== + dependencies: + "@emnapi/wasi-threads" "1.1.0" + tslib "^2.4.0" + +"@emnapi/runtime@^1.4.3": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.5.0.tgz#9aebfcb9b17195dce3ab53c86787a6b7d058db73" + integrity sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== + dependencies: + tslib "^2.4.0" + +"@esbuild/aix-ppc64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz#ee6b7163a13528e099ecf562b972f2bcebe0aa97" + integrity sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw== + +"@esbuild/android-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz#115fc76631e82dd06811bfaf2db0d4979c16e2cb" + integrity sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg== + +"@esbuild/android-arm@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.10.tgz#8d5811912da77f615398611e5bbc1333fe321aa9" + integrity sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w== + +"@esbuild/android-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.10.tgz#e3e96516b2d50d74105bb92594c473e30ddc16b1" + integrity sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg== + +"@esbuild/darwin-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz#6af6bb1d05887dac515de1b162b59dc71212ed76" + integrity sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA== + +"@esbuild/darwin-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz#99ae82347fbd336fc2d28ffd4f05694e6e5b723d" + integrity sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg== + +"@esbuild/freebsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz#0c6d5558a6322b0bdb17f7025c19bd7d2359437d" + integrity sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg== + +"@esbuild/freebsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz#8c35873fab8c0857a75300a3dcce4324ca0b9844" + integrity sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA== + +"@esbuild/linux-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz#3edc2f87b889a15b4cedaf65f498c2bed7b16b90" + integrity sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ== + +"@esbuild/linux-arm@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz#86501cfdfb3d110176d80c41b27ed4611471cde7" + integrity sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg== + +"@esbuild/linux-ia32@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz#e6589877876142537c6864680cd5d26a622b9d97" + integrity sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ== + +"@esbuild/linux-loong64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz#11119e18781f136d8083ea10eb6be73db7532de8" + integrity sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg== + +"@esbuild/linux-mips64el@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz#3052f5436b0c0c67a25658d5fc87f045e7def9e6" + integrity sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA== + +"@esbuild/linux-ppc64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz#2f098920ee5be2ce799f35e367b28709925a8744" + integrity sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA== + +"@esbuild/linux-riscv64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz#fa51d7fd0a22a62b51b4b94b405a3198cf7405dd" + integrity sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA== + +"@esbuild/linux-s390x@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz#a27642e36fc282748fdb38954bd3ef4f85791e8a" + integrity sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew== + +"@esbuild/linux-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz#9d9b09c0033d17529570ced6d813f98315dfe4e9" + integrity sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA== + +"@esbuild/netbsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz#25c09a659c97e8af19e3f2afd1c9190435802151" + integrity sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A== + +"@esbuild/netbsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz#7fa5f6ffc19be3a0f6f5fd32c90df3dc2506937a" + integrity sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig== + +"@esbuild/openbsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz#8faa6aa1afca0c6d024398321d6cb1c18e72a1c3" + integrity sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw== + +"@esbuild/openbsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz#a42979b016f29559a8453d32440d3c8cd420af5e" + integrity sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw== + +"@esbuild/openharmony-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz#fd87bfeadd7eeb3aa384bbba907459ffa3197cb1" + integrity sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag== + +"@esbuild/sunos-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz#3a18f590e36cb78ae7397976b760b2b8c74407f4" + integrity sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ== + +"@esbuild/win32-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz#e71741a251e3fd971408827a529d2325551f530c" + integrity sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw== + +"@esbuild/win32-ia32@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz#c6f010b5d3b943d8901a0c87ea55f93b8b54bf94" + integrity sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw== + +"@esbuild/win32-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz#e4b3e255a1b4aea84f6e1d2ae0b73f826c3785bd" + integrity sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw== + +"@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" + integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.1.tgz#d316e47905bd0a1a931fa50e669b9af4104d1617" + integrity sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA== + +"@eslint/core@^0.15.2": + version "0.15.2" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f" + integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.36.0", "@eslint/js@^9.32.0": + version "9.36.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.36.0.tgz#b1a3893dd6ce2defed5fd49de805ba40368e8fef" + integrity sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5" + integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w== + dependencies: + "@eslint/core" "^0.15.2" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@napi-rs/wasm-runtime@^0.2.5": + version "0.2.12" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2" + integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== + dependencies: + "@emnapi/core" "^1.4.3" + "@emnapi/runtime" "^1.4.3" + "@tybys/wasm-util" "^0.10.0" + +"@node-rs/crc32-android-arm-eabi@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm-eabi/-/crc32-android-arm-eabi-1.10.6.tgz#9dbd6ee79247adfd83f9d020be98659b201b92fc" + integrity sha512-vZAMuJXm3TpWPOkkhxdrofWDv+Q+I2oO7ucLRbXyAPmXFNDhHtBxbO1rk9Qzz+M3eep8ieS4/+jCL1Q0zacNMQ== + +"@node-rs/crc32-android-arm64@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-android-arm64/-/crc32-android-arm64-1.10.6.tgz#98947ca70639d14b67e26ccdf89963c4b2cc192f" + integrity sha512-Vl/JbjCinCw/H9gEpZveWCMjxjcEChDcDBM8S4hKay5yyoRCUHJPuKr4sjVDBeOm+1nwU3oOm6Ca8dyblwp4/w== + +"@node-rs/crc32-darwin-arm64@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-arm64/-/crc32-darwin-arm64-1.10.6.tgz#c764dac07a055ce5ed4aa21d6f6f06977a05ff63" + integrity sha512-kARYANp5GnmsQiViA5Qu74weYQ3phOHSYQf0G+U5wB3NB5JmBHnZcOc46Ig21tTypWtdv7u63TaltJQE41noyg== + +"@node-rs/crc32-darwin-x64@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-darwin-x64/-/crc32-darwin-x64-1.10.6.tgz#3e48333cb88e1a23283780cd01a86bc3114b2598" + integrity sha512-Q99bevJVMfLTISpkpKBlXgtPUItrvTWKFyiqoKH5IvscZmLV++NH4V13Pa17GTBmv9n18OwzgQY4/SRq6PQNVA== + +"@node-rs/crc32-freebsd-x64@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-freebsd-x64/-/crc32-freebsd-x64-1.10.6.tgz#632cb04e2f37830f79246f7b4c78ace2826ab07c" + integrity sha512-66hpawbNjrgnS9EDMErta/lpaqOMrL6a6ee+nlI2viduVOmRZWm9Rg9XdGTK/+c4bQLdtC6jOd+Kp4EyGRYkAg== + +"@node-rs/crc32-linux-arm-gnueabihf@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm-gnueabihf/-/crc32-linux-arm-gnueabihf-1.10.6.tgz#21753195058d57127cb8e50da6ac42aa978f854f" + integrity sha512-E8Z0WChH7X6ankbVm8J/Yym19Cq3otx6l4NFPS6JW/cWdjv7iw+Sps2huSug+TBprjbcEA+s4TvEwfDI1KScjg== + +"@node-rs/crc32-linux-arm64-gnu@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-gnu/-/crc32-linux-arm64-gnu-1.10.6.tgz#e305432589c802d9f17ab8a90d62c89ef8c9a7d7" + integrity sha512-LmWcfDbqAvypX0bQjQVPmQGazh4dLiVklkgHxpV4P0TcQ1DT86H/SWpMBMs/ncF8DGuCQ05cNyMv1iddUDugoQ== + +"@node-rs/crc32-linux-arm64-musl@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-arm64-musl/-/crc32-linux-arm64-musl-1.10.6.tgz#cd2390f015009310004428eb2c840e779aae4759" + integrity sha512-k8ra/bmg0hwRrIEE8JL1p32WfaN9gDlUUpQRWsbxd1WhjqvXea7kKO6K4DwVxyxlPhBS9Gkb5Urq7Y4mXANzaw== + +"@node-rs/crc32-linux-x64-gnu@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-gnu/-/crc32-linux-x64-gnu-1.10.6.tgz#1993c13ab466f9607b0bc23737f9321ecdd7eeae" + integrity sha512-IfjtqcuFK7JrSZ9mlAFhb83xgium30PguvRjIMI45C3FJwu18bnLk1oR619IYb/zetQT82MObgmqfKOtgemEKw== + +"@node-rs/crc32-linux-x64-musl@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-linux-x64-musl/-/crc32-linux-x64-musl-1.10.6.tgz#507808fbe1386fb728c720a654018b48afb65e91" + integrity sha512-LbFYsA5M9pNunOweSt6uhxenYQF94v3bHDAQRPTQ3rnjn+mK6IC7YTAYoBjvoJP8lVzcvk9hRj8wp4Jyh6Y80g== + +"@node-rs/crc32-wasm32-wasi@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-wasm32-wasi/-/crc32-wasm32-wasi-1.10.6.tgz#faf5c97172f7b77098d130b8211ffa4924b6ad29" + integrity sha512-KaejdLgHMPsRaxnM+OG9L9XdWL2TabNx80HLdsCOoX9BVhEkfh39OeahBo8lBmidylKbLGMQoGfIKDjq0YMStw== + dependencies: + "@napi-rs/wasm-runtime" "^0.2.5" + +"@node-rs/crc32-win32-arm64-msvc@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-arm64-msvc/-/crc32-win32-arm64-msvc-1.10.6.tgz#3ed70ad1b528caeca26c5f95f694926b0039d135" + integrity sha512-x50AXiSxn5Ccn+dCjLf1T7ZpdBiV1Sp5aC+H2ijhJO4alwznvXgWbopPRVhbp2nj0i+Gb6kkDUEyU+508KAdGQ== + +"@node-rs/crc32-win32-ia32-msvc@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-ia32-msvc/-/crc32-win32-ia32-msvc-1.10.6.tgz#030cd40632c5b8e63c4f62a7b5b4ecf8614af887" + integrity sha512-DpDxQLaErJF9l36aghe1Mx+cOnYLKYo6qVPqPL9ukJ5rAGLtCdU0C+Zoi3gs9ySm8zmbFgazq/LvmsZYU42aBw== + +"@node-rs/crc32-win32-x64-msvc@1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32-win32-x64-msvc/-/crc32-win32-x64-msvc-1.10.6.tgz#d4ed8261fe7a935bd01ce368c4d424dd60f1082f" + integrity sha512-5B1vXosIIBw1m2Rcnw62IIfH7W9s9f7H7Ma0rRuhT8HR4Xh8QCgw6NJSI2S2MCngsGktYnAhyUvs81b7efTyQw== + +"@node-rs/crc32@^1.7.0": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@node-rs/crc32/-/crc32-1.10.6.tgz#3e9916ae63b7c54313688c5535582a3f55ae953d" + integrity sha512-+llXfqt+UzgoDzT9of5vPQPGqTAVCohU74I9zIBkNo5TH6s2P31DFJOGsJQKN207f0GHnYv5pV3wh3BCY/un/A== + optionalDependencies: + "@node-rs/crc32-android-arm-eabi" "1.10.6" + "@node-rs/crc32-android-arm64" "1.10.6" + "@node-rs/crc32-darwin-arm64" "1.10.6" + "@node-rs/crc32-darwin-x64" "1.10.6" + "@node-rs/crc32-freebsd-x64" "1.10.6" + "@node-rs/crc32-linux-arm-gnueabihf" "1.10.6" + "@node-rs/crc32-linux-arm64-gnu" "1.10.6" + "@node-rs/crc32-linux-arm64-musl" "1.10.6" + "@node-rs/crc32-linux-x64-gnu" "1.10.6" + "@node-rs/crc32-linux-x64-musl" "1.10.6" + "@node-rs/crc32-wasm32-wasi" "1.10.6" + "@node-rs/crc32-win32-arm64-msvc" "1.10.6" + "@node-rs/crc32-win32-ia32-msvc" "1.10.6" + "@node-rs/crc32-win32-x64-msvc" "1.10.6" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgr/core@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== + +"@secretlint/config-creator@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/config-creator/-/config-creator-10.2.2.tgz#5d646e83bb2aacfbd5218968ceb358420b4c2cb3" + integrity sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ== + dependencies: + "@secretlint/types" "^10.2.2" + +"@secretlint/config-loader@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/config-loader/-/config-loader-10.2.2.tgz#a7790c8d0301db4f6d47e6fb0f0f9482fe652d9a" + integrity sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ== + dependencies: + "@secretlint/profiler" "^10.2.2" + "@secretlint/resolver" "^10.2.2" + "@secretlint/types" "^10.2.2" + ajv "^8.17.1" + debug "^4.4.1" + rc-config-loader "^4.1.3" + +"@secretlint/core@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/core/-/core-10.2.2.tgz#cd41d5c27ba07c217f0af4e0e24dbdfe5ef62042" + integrity sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw== + dependencies: + "@secretlint/profiler" "^10.2.2" + "@secretlint/types" "^10.2.2" + debug "^4.4.1" + structured-source "^4.0.0" + +"@secretlint/formatter@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/formatter/-/formatter-10.2.2.tgz#c8ce35803ad0d841cc9b6e703d6fab68a144e9c0" + integrity sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA== + dependencies: + "@secretlint/resolver" "^10.2.2" + "@secretlint/types" "^10.2.2" + "@textlint/linter-formatter" "^15.2.0" + "@textlint/module-interop" "^15.2.0" + "@textlint/types" "^15.2.0" + chalk "^5.4.1" + debug "^4.4.1" + pluralize "^8.0.0" + strip-ansi "^7.1.0" + table "^6.9.0" + terminal-link "^4.0.0" + +"@secretlint/node@^10.1.1", "@secretlint/node@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/node/-/node-10.2.2.tgz#1d8a6ed620170bf4f29829a3a91878682c43c4d9" + integrity sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ== + dependencies: + "@secretlint/config-loader" "^10.2.2" + "@secretlint/core" "^10.2.2" + "@secretlint/formatter" "^10.2.2" + "@secretlint/profiler" "^10.2.2" + "@secretlint/source-creator" "^10.2.2" + "@secretlint/types" "^10.2.2" + debug "^4.4.1" + p-map "^7.0.3" + +"@secretlint/profiler@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/profiler/-/profiler-10.2.2.tgz#82c085ab1966806763bbf6edb830987f25d4e797" + integrity sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig== + +"@secretlint/resolver@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/resolver/-/resolver-10.2.2.tgz#9c3c3e2fef00679fcce99793e76e19e575b75721" + integrity sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w== + +"@secretlint/secretlint-formatter-sarif@^10.1.1": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz#5c4044a6a6c9d95e2f57270d6184931f0979d649" + integrity sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ== + dependencies: + node-sarif-builder "^3.2.0" + +"@secretlint/secretlint-rule-no-dotenv@^10.1.1": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz#ea43dcc2abd1dac3288b056610361f319f5ce6e9" + integrity sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg== + dependencies: + "@secretlint/types" "^10.2.2" + +"@secretlint/secretlint-rule-preset-recommend@^10.1.1": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz#27b17c38b360c6788826d28fcda28ac6e9772d0b" + integrity sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA== + +"@secretlint/source-creator@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/source-creator/-/source-creator-10.2.2.tgz#d600b6d4487859cdd39bbb1cf8cf744540b3f7a1" + integrity sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw== + dependencies: + "@secretlint/types" "^10.2.2" + istextorbinary "^9.5.0" + +"@secretlint/types@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@secretlint/types/-/types-10.2.2.tgz#1412d8f699fd900182cbf4c2923a9df9eb321ca7" + integrity sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg== + +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + +"@textlint/ast-node-types@15.2.2": + version "15.2.2" + resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-15.2.2.tgz#450632e54d08fc6776a620671a0ae29bb6cec08b" + integrity sha512-9ByYNzWV8tpz6BFaRzeRzIov8dkbSZu9q7IWqEIfmRuLWb2qbI/5gTvKcoWT1HYs4XM7IZ8TKSXcuPvMb6eorA== + +"@textlint/linter-formatter@^15.2.0": + version "15.2.2" + resolved "https://registry.yarnpkg.com/@textlint/linter-formatter/-/linter-formatter-15.2.2.tgz#d09311cdba9b48eef3800dca77ac12c4e653ad3a" + integrity sha512-oMVaMJ3exFvXhCj3AqmCbLaeYrTNLqaJnLJMIlmnRM3/kZdxvku4OYdaDzgtlI194cVxamOY5AbHBBVnY79kEg== + dependencies: + "@azu/format-text" "^1.0.2" + "@azu/style-format" "^1.0.1" + "@textlint/module-interop" "15.2.2" + "@textlint/resolver" "15.2.2" + "@textlint/types" "15.2.2" + chalk "^4.1.2" + debug "^4.4.1" + js-yaml "^3.14.1" + lodash "^4.17.21" + pluralize "^2.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + table "^6.9.0" + text-table "^0.2.0" + +"@textlint/module-interop@15.2.2", "@textlint/module-interop@^15.2.0": + version "15.2.2" + resolved "https://registry.yarnpkg.com/@textlint/module-interop/-/module-interop-15.2.2.tgz#cffb4162265813a5813d5c807d9faae778638d3f" + integrity sha512-2rmNcWrcqhuR84Iio1WRzlc4tEoOMHd6T7urjtKNNefpTt1owrTJ9WuOe60yD3FrTW0J/R0ux5wxUbP/eaeFOA== + +"@textlint/resolver@15.2.2": + version "15.2.2" + resolved "https://registry.yarnpkg.com/@textlint/resolver/-/resolver-15.2.2.tgz#4871c590aa3e633b512efa5978e96e96bf7fb9b8" + integrity sha512-4hGWjmHt0y+5NAkoYZ8FvEkj8Mez9TqfbTm3BPjoV32cIfEixl2poTOgapn1rfm73905GSO3P1jiWjmgvii13Q== + +"@textlint/types@15.2.2", "@textlint/types@^15.2.0": + version "15.2.2" + resolved "https://registry.yarnpkg.com/@textlint/types/-/types-15.2.2.tgz#9e47d1021ece2cefe1749131b054e1a332cabcda" + integrity sha512-X2BHGAR3yXJsCAjwYEDBIk9qUDWcH4pW61ISfmtejau+tVqKtnbbvEZnMTb6mWgKU1BvTmftd5DmB1XVDUtY3g== + dependencies: + "@textlint/ast-node-types" "15.2.2" + +"@tybys/wasm-util@^0.10.0": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + dependencies: + tslib "^2.4.0" + +"@types/estree@^1.0.6": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@20.x": + version "20.19.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.17.tgz#41b52697373aef8a43b3b92f33b43f329b2d674b" + integrity sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ== + dependencies: + undici-types "~6.21.0" + +"@types/normalize-package-data@^2.4.3": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + +"@types/sarif@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@types/sarif/-/sarif-2.1.7.tgz#dab4d16ba7568e9846c454a8764f33c5d98e5524" + integrity sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ== + +"@types/vscode@^1.102.0": + version "1.104.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.104.0.tgz#6e4d041d1ff5722db72c688518b330fcfb6fc658" + integrity sha512-0KwoU2rZ2ecsTGFxo4K1+f+AErRsYW0fsp6A0zufzGuhyczc2IoKqYqcwXidKXmy2u8YB2GsYsOtiI9Izx3Tig== + +"@typescript-eslint/eslint-plugin@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz#d72bf8b2d3052afee919ba38f38c57138eee0396" + integrity sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.44.0" + "@typescript-eslint/type-utils" "8.44.0" + "@typescript-eslint/utils" "8.44.0" + "@typescript-eslint/visitor-keys" "8.44.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/parser@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.44.0.tgz#0436fbe0a72f86d3366d2d157d480524b0ab3f26" + integrity sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw== + dependencies: + "@typescript-eslint/scope-manager" "8.44.0" + "@typescript-eslint/types" "8.44.0" + "@typescript-eslint/typescript-estree" "8.44.0" + "@typescript-eslint/visitor-keys" "8.44.0" + debug "^4.3.4" + +"@typescript-eslint/project-service@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.44.0.tgz#89060651dcecde946e758441fe94dceb6f769a29" + integrity sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.44.0" + "@typescript-eslint/types" "^8.44.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz#c37f1e786fd0e5b40607985c769a61c24c761c26" + integrity sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA== + dependencies: + "@typescript-eslint/types" "8.44.0" + "@typescript-eslint/visitor-keys" "8.44.0" + +"@typescript-eslint/tsconfig-utils@8.44.0", "@typescript-eslint/tsconfig-utils@^8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz#8c0601372bf889f0663a08df001ad666442aa3a8" + integrity sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ== + +"@typescript-eslint/type-utils@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz#5b875f8a961d15bb47df787cbfde50baea312613" + integrity sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg== + dependencies: + "@typescript-eslint/types" "8.44.0" + "@typescript-eslint/typescript-estree" "8.44.0" + "@typescript-eslint/utils" "8.44.0" + debug "^4.3.4" + ts-api-utils "^2.1.0" + +"@typescript-eslint/types@8.44.0", "@typescript-eslint/types@^8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.44.0.tgz#4b9154ab164a0beff22d3217ff0fdc8d10bce924" + integrity sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA== + +"@typescript-eslint/typescript-estree@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz#e23e9946c466cf5f53b7e46ecdd9789fd8192daa" + integrity sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw== + dependencies: + "@typescript-eslint/project-service" "8.44.0" + "@typescript-eslint/tsconfig-utils" "8.44.0" + "@typescript-eslint/types" "8.44.0" + "@typescript-eslint/visitor-keys" "8.44.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" + +"@typescript-eslint/utils@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.44.0.tgz#2c0650a1e8a832ed15658e7ca3c7bd2818d92c7c" + integrity sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.44.0" + "@typescript-eslint/types" "8.44.0" + "@typescript-eslint/typescript-estree" "8.44.0" + +"@typescript-eslint/visitor-keys@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz#0d9d5647e005c2ff8acc391d1208ab37d08850aa" + integrity sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw== + dependencies: + "@typescript-eslint/types" "8.44.0" + eslint-visitor-keys "^4.2.1" + +"@typespec/ts-http-runtime@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.1.tgz#2fa94050f25b4d85d0bc8b9d97874b8d347a9173" + integrity sha512-SnbaqayTVFEA6/tYumdF0UmybY0KHyKwGPBXnyckFlrrKdhWFrL3a2HIPXHjht5ZOElKGcXfD2D63P36btb+ww== + dependencies: + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + +"@vscode/vsce-sign-alpine-arm64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.5.tgz#e34cbf91f4e86a6cf52abc2e6e75084ae18f6c4a" + integrity sha512-XVmnF40APwRPXSLYA28Ye+qWxB25KhSVpF2eZVtVOs6g7fkpOxsVnpRU1Bz2xG4ySI79IRuapDJoAQFkoOgfdQ== + +"@vscode/vsce-sign-alpine-x64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.5.tgz#7443c0e839e74f03fce0cc3145330f0d2a80cc87" + integrity sha512-JuxY3xcquRsOezKq6PEHwCgd1rh1GnhyH6urVEWUzWn1c1PC4EOoyffMD+zLZtFuZF5qR1I0+cqDRNKyPvpK7Q== + +"@vscode/vsce-sign-darwin-arm64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.5.tgz#2eabac7d8371292a8d22a15b3ff57f1988c29d6b" + integrity sha512-z2Q62bk0ptADFz8a0vtPvnm6vxpyP3hIEYMU+i1AWz263Pj8Mc38cm/4sjzxu+LIsAfhe9HzvYNS49lV+KsatQ== + +"@vscode/vsce-sign-darwin-x64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.5.tgz#96fb0329c8a367184c203d62574f9a92193022d8" + integrity sha512-ma9JDC7FJ16SuPXlLKkvOD2qLsmW/cKfqK4zzM2iJE1PbckF3BlR08lYqHV89gmuoTpYB55+z8Y5Fz4wEJBVDA== + +"@vscode/vsce-sign-linux-arm64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.5.tgz#c0450232aba43fbeadff5309838a5655dc7039c8" + integrity sha512-Hr1o0veBymg9SmkCqYnfaiUnes5YK6k/lKFA5MhNmiEN5fNqxyPUCdRZMFs3Ajtx2OFW4q3KuYVRwGA7jdLo7Q== + +"@vscode/vsce-sign-linux-arm@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.5.tgz#bf07340db1fe35cb3a8a222b2da4aa25310ee251" + integrity sha512-cdCwtLGmvC1QVrkIsyzv01+o9eR+wodMJUZ9Ak3owhcGxPRB53/WvrDHAFYA6i8Oy232nuen1YqWeEohqBuSzA== + +"@vscode/vsce-sign-linux-x64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.5.tgz#23829924f40867e90d5e3bb861e8e8fa045eb0ee" + integrity sha512-XLT0gfGMcxk6CMRLDkgqEPTyG8Oa0OFe1tPv2RVbphSOjFWJwZgK3TYWx39i/7gqpDHlax0AP6cgMygNJrA6zg== + +"@vscode/vsce-sign-win32-arm64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.5.tgz#18ef271f5f7d9b31c03127582c1b1c51f26e23b4" + integrity sha512-hco8eaoTcvtmuPhavyCZhrk5QIcLiyAUhEso87ApAWDllG7djIrWiOCtqn48k4pHz+L8oCQlE0nwNHfcYcxOPw== + +"@vscode/vsce-sign-win32-x64@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.5.tgz#83b89393e4451cfa7e3a2182aea4250f5e71aca8" + integrity sha512-1ixKFGM2FwM+6kQS2ojfY3aAelICxjiCzeg4nTHpkeU1Tfs4RC+lVLrgq5NwcBC7ZLr6UfY3Ct3D6suPeOf7BQ== + +"@vscode/vsce-sign@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@vscode/vsce-sign/-/vsce-sign-2.0.6.tgz#a2b11e29dab56379c513e0cc52615edad1d34cd3" + integrity sha512-j9Ashk+uOWCDHYDxgGsqzKq5FXW9b9MW7QqOIYZ8IYpneJclWTBeHZz2DJCSKQgo+JAqNcaRRE1hzIx0dswqAw== + optionalDependencies: + "@vscode/vsce-sign-alpine-arm64" "2.0.5" + "@vscode/vsce-sign-alpine-x64" "2.0.5" + "@vscode/vsce-sign-darwin-arm64" "2.0.5" + "@vscode/vsce-sign-darwin-x64" "2.0.5" + "@vscode/vsce-sign-linux-arm" "2.0.5" + "@vscode/vsce-sign-linux-arm64" "2.0.5" + "@vscode/vsce-sign-linux-x64" "2.0.5" + "@vscode/vsce-sign-win32-arm64" "2.0.5" + "@vscode/vsce-sign-win32-x64" "2.0.5" + +"@vscode/vsce@^3.2.1", "@vscode/vsce@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-3.6.0.tgz#7102cb846db83ed70ec7119986af7d7c69cf3538" + integrity sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg== + dependencies: + "@azure/identity" "^4.1.0" + "@secretlint/node" "^10.1.1" + "@secretlint/secretlint-formatter-sarif" "^10.1.1" + "@secretlint/secretlint-rule-no-dotenv" "^10.1.1" + "@secretlint/secretlint-rule-preset-recommend" "^10.1.1" + "@vscode/vsce-sign" "^2.0.0" + azure-devops-node-api "^12.5.0" + chalk "^4.1.2" + cheerio "^1.0.0-rc.9" + cockatiel "^3.1.2" + commander "^12.1.0" + form-data "^4.0.0" + glob "^11.0.0" + hosted-git-info "^4.0.2" + jsonc-parser "^3.2.0" + leven "^3.1.0" + markdown-it "^14.1.0" + mime "^1.3.4" + minimatch "^3.0.3" + parse-semver "^1.1.1" + read "^1.0.7" + secretlint "^10.1.1" + semver "^7.5.2" + tmp "^0.2.3" + typed-rest-client "^1.8.4" + url-join "^4.0.1" + xml2js "^0.5.0" + yauzl "^2.3.1" + yazl "^2.2.2" + optionalDependencies: + keytar "^7.7.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.1, ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-escapes@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.1.0.tgz#91983a524b64e49f8e46fb962bfb7f375ced2ad5" + integrity sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g== + dependencies: + environment "^1.0.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +azure-devops-node-api@^12.5.0: + version "12.5.0" + resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz#38b9efd7c5ac74354fe4e8dbe42697db0b8e85a5" + integrity sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og== + dependencies: + tunnel "0.0.6" + typed-rest-client "^1.8.4" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binaryextensions@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-6.11.0.tgz#c36b3e6b5c59e621605709b099cda8dda824cc72" + integrity sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw== + dependencies: + editions "^6.21.0" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boundary@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/boundary/-/boundary-2.0.0.tgz#169c8b1f0d44cf2c25938967a328f37e0a4e5efc" + integrity sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.4.1: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@^1.0.0-rc.9: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.1.2.tgz#26af77e89336c81c63ea83197f868b4cbd351369" + integrity sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.2.2" + encoding-sniffer "^0.2.1" + htmlparser2 "^10.0.0" + parse5 "^7.3.0" + parse5-htmlparser2-tree-adapter "^7.1.0" + parse5-parser-stream "^7.1.2" + undici "^7.12.0" + whatwg-mimetype "^4.0.0" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cockatiel@^3.1.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/cockatiel/-/cockatiel-3.2.1.tgz#575f937bc4040a20ae27352a6d07c9c5a741981f" + integrity sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q== + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-select@^5.1.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.2.2.tgz#01b6e8d163637bb2dd6c982ca4ed65863682786e" + integrity sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-what@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea" + integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA== + +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-data-property@^1.0.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-libc@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.0.tgz#3ca811f60a7b504b0480e5008adacc660b0b8c4f" + integrity sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1, domutils@^3.2.1, domutils@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +editions@^6.21.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/editions/-/editions-6.22.0.tgz#3913c4eea9aa4586e17bcd25d64d5edf1790657a" + integrity sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ== + dependencies: + version-range "^4.15.0" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encoding-sniffer@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819" + integrity sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw== + dependencies: + iconv-lite "^0.6.3" + whatwg-encoding "^3.1.1" + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +entities@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== + +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +esbuild@^0.25.3: + version "0.25.10" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.10.tgz#37f5aa5cd14500f141be121c01b096ca83ac34a9" + integrity sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.10" + "@esbuild/android-arm" "0.25.10" + "@esbuild/android-arm64" "0.25.10" + "@esbuild/android-x64" "0.25.10" + "@esbuild/darwin-arm64" "0.25.10" + "@esbuild/darwin-x64" "0.25.10" + "@esbuild/freebsd-arm64" "0.25.10" + "@esbuild/freebsd-x64" "0.25.10" + "@esbuild/linux-arm" "0.25.10" + "@esbuild/linux-arm64" "0.25.10" + "@esbuild/linux-ia32" "0.25.10" + "@esbuild/linux-loong64" "0.25.10" + "@esbuild/linux-mips64el" "0.25.10" + "@esbuild/linux-ppc64" "0.25.10" + "@esbuild/linux-riscv64" "0.25.10" + "@esbuild/linux-s390x" "0.25.10" + "@esbuild/linux-x64" "0.25.10" + "@esbuild/netbsd-arm64" "0.25.10" + "@esbuild/netbsd-x64" "0.25.10" + "@esbuild/openbsd-arm64" "0.25.10" + "@esbuild/openbsd-x64" "0.25.10" + "@esbuild/openharmony-arm64" "0.25.10" + "@esbuild/sunos-x64" "0.25.10" + "@esbuild/win32-arm64" "0.25.10" + "@esbuild/win32-ia32" "0.25.10" + "@esbuild/win32-x64" "0.25.10" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-prettier@^5.5.3: + version "5.5.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz#9d61c4ea11de5af704d4edf108c82ccfa7f2e61c" + integrity sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.11.7" + +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^9.30.1: + version "9.36.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.36.0.tgz#9cc5cbbfb9c01070425d9bfed81b4e79a1c09088" + integrity sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.0" + "@eslint/config-helpers" "^0.3.1" + "@eslint/core" "^0.15.2" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.36.0" + "@eslint/plugin-kit" "^0.3.5" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@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.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.3.2, fast-glob@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +follow-redirects@^1.14.6: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +foreground-child@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +form-data@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== + 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" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^11.1.1: + version "11.3.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4" + integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== + dependencies: + foreground-child "^3.3.1" + jackspeak "^4.1.1" + minimatch "^10.0.3" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globalthis@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.1.0.tgz#138b78e77cf5a8d794e327b15dce80bf1fb0a73e" + integrity sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.3" + ignore "^7.0.3" + path-type "^6.0.0" + slash "^5.1.0" + unicorn-magic "^0.3.0" + +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hosted-git-info@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + +htmlparser2@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.0.0.tgz#77ad249037b66bf8cc99c6e286ef73b83aeb621d" + integrity sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.2.1" + entities "^6.0.0" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + +iconv-lite@0.6.3, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +ignore@^7.0.0, ignore@^7.0.3: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +index-to-position@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-1.1.0.tgz#2e50bd54c8040bdd6d9b3d95ec2a8fedf86b4d44" + integrity sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-it-type@^5.1.2: + version "5.1.3" + resolved "https://registry.yarnpkg.com/is-it-type/-/is-it-type-5.1.3.tgz#2606b194e661d26f01c393218fde76c19d928059" + integrity sha512-AX2uU0HW+TxagTgQXOJY7+2fbFHemC7YFBwN1XqD8qQMKdtfbOC8OC3fUb4s5NU59a3662Dzwto8tWDdZYRXxg== + dependencies: + globalthis "^1.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +istextorbinary@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-9.5.0.tgz#e6e13febf1c1685100ae264809a4f8f46e01dfd3" + integrity sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw== + dependencies: + binaryextensions "^6.11.0" + editions "^6.21.0" + textextensions "^6.11.0" + +jackspeak@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" + integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + +jsonfile@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62" + integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonwebtoken@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + +jwa@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" + integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keytar@^7.7.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" + integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ== + dependencies: + node-addon-api "^4.3.0" + prebuild-install "^7.0.1" + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^10.0.1: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^11.0.0: + version "11.2.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.1.tgz#d426ac471521729c6c1acda5f7a633eadaa28db2" + integrity sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +markdown-it@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + +minimatch@^3.0.3, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mute-stream@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +napi-build-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" + integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +node-abi@^3.3.0: + version "3.77.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.77.0.tgz#3ad90d5c9d45663420e5aa4ff58dbf4e3625419a" + integrity sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ== + dependencies: + semver "^7.3.5" + +node-addon-api@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" + integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== + +node-sarif-builder@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz#ba008995d8b165570c3f38300e56299a93531db1" + integrity sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q== + dependencies: + "@types/sarif" "^2.1.7" + fs-extra "^11.1.1" + +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^10.1.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" + integrity sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + wsl-utils "^0.1.0" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +ovsx@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/ovsx/-/ovsx-0.10.5.tgz#dc06e0a8092bf582af304b5ae271e784a32f2d03" + integrity sha512-jfulG5k9vjWcolg2kubC51t1eHKA8ANPcKCQKaWPfOsJZ9VlIppP0Anf8pJ1LJHZFHoRmeMXITG9a5NXHwY9tA== + dependencies: + "@vscode/vsce" "^3.2.1" + commander "^6.2.1" + follow-redirects "^1.14.6" + is-ci "^2.0.0" + leven "^3.1.0" + semver "^7.6.0" + tmp "^0.2.3" + yauzl-promise "^4.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.3.tgz#7ac210a2d36f81ec28b736134810f7ba4418cdb6" + integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.3.0.tgz#88a195a2157025139a2317a4f2f9252b61304ed5" + integrity sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ== + dependencies: + "@babel/code-frame" "^7.26.2" + index-to-position "^1.1.0" + type-fest "^4.39.1" + +parse-semver@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" + integrity sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ== + dependencies: + semver "^5.1.0" + +parse5-htmlparser2-tree-adapter@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b" + integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g== + dependencies: + domhandler "^5.0.3" + parse5 "^7.0.0" + +parse5-parser-stream@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" + integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== + dependencies: + parse5 "^7.0.0" + +parse5@^7.0.0, parse5@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + +path-type@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-6.0.0.tgz#2f1bb6791a91ce99194caede5d6c5920ed81eb51" + integrity sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pluralize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-2.0.0.tgz#72b726aa6fac1edeee42256c7d8dc256b335677f" + integrity sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +prebuild-install@^7.0.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" + integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^2.0.0" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== + +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@^6.9.1: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +rc-config-loader@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/rc-config-loader/-/rc-config-loader-4.1.3.tgz#1352986b8a2d8d96d6fd054a5bb19a60c576876a" + integrity sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w== + dependencies: + debug "^4.3.4" + js-yaml "^4.1.0" + json5 "^2.2.2" + require-from-string "^2.0.2" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== + dependencies: + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" + +read@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ== + dependencies: + mute-stream "~0.0.4" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +run-applescript@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.1.0.tgz#2e9e54c4664ec3106c5b5630e249d3d6595c4911" + integrity sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +secretlint@^10.1.1: + version "10.2.2" + resolved "https://registry.yarnpkg.com/secretlint/-/secretlint-10.2.2.tgz#c0cf997153a2bef0b653874dc87030daa6a35140" + integrity sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg== + dependencies: + "@secretlint/config-creator" "^10.2.2" + "@secretlint/formatter" "^10.2.2" + "@secretlint/node" "^10.2.2" + "@secretlint/profiler" "^10.2.2" + debug "^4.4.1" + globby "^14.1.0" + read-pkg "^9.0.1" + +semver@^5.1.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^7.3.5, semver@^7.5.2, semver@^7.5.4, semver@^7.6.0: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-invariant@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/simple-invariant/-/simple-invariant-2.0.1.tgz#b8935284d31bc0c2719582f9cddf17bee8f57526" + integrity sha512-1sbhsxqI+I2tqlmjbz99GXNmZtr6tKIyEgGGnJw/MKGblalqk/XoOYYFJlBzTKZCxx8kLaD3FD5s9BEEjx5Pyg== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.22" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +structured-source@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/structured-source/-/structured-source-4.0.0.tgz#0c9e59ee43dedd8fc60a63731f60e358102a4948" + integrity sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA== + dependencies: + boundary "^2.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz#b8e485b179681dea496a1e7abdf8985bd3145461" + integrity sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +synckit@^0.11.7: + version "0.11.11" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" + integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== + dependencies: + "@pkgr/core" "^0.2.9" + +table@^6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" + integrity sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tar-fs@^2.0.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.4.tgz#800824dbf4ef06ded9afea4acafe71c67c76b930" + integrity sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +terminal-link@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-4.0.0.tgz#5f3e50329420fad97d07d624f7df1851d82963f1" + integrity sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA== + dependencies: + ansi-escapes "^7.0.0" + supports-hyperlinks "^3.2.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +textextensions@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-6.11.0.tgz#864535d09f49026150c96f0b0d79f1fa0869db15" + integrity sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ== + dependencies: + editions "^6.21.0" + +tmp@^0.2.3: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +tslib@^2.2.0, tslib@^2.4.0, tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^4.39.1, type-fest@^4.6.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + +typed-rest-client@^1.8.4: + version "1.8.11" + resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.11.tgz#6906f02e3c91e8d851579f255abf0fd60800a04d" + integrity sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA== + dependencies: + qs "^6.9.1" + tunnel "0.0.6" + underscore "^1.12.1" + +typescript-eslint@^8.38.0: + version "8.44.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.44.0.tgz#5f052fa52af2420fdc488ab4cabd823f4f8594c8" + integrity sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw== + dependencies: + "@typescript-eslint/eslint-plugin" "8.44.0" + "@typescript-eslint/parser" "8.44.0" + "@typescript-eslint/typescript-estree" "8.44.0" + "@typescript-eslint/utils" "8.44.0" + +typescript@^5.8.3: + version "5.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" + integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +underscore@^1.12.1: + version "1.13.7" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +undici@^7.12.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.16.0.tgz#cb2a1e957726d458b536e3f076bf51f066901c1a" + integrity sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +version-range@^4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/version-range/-/version-range-4.15.0.tgz#89df1e921b14d37515aab5e42ed4ac0515cab2c1" + integrity sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +wsl-utils@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/wsl-utils/-/wsl-utils-0.1.0.tgz#8783d4df671d4d50365be2ee4c71917a0557baab" + integrity sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw== + dependencies: + is-wsl "^3.1.0" + +xml2js@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yauzl-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yauzl-promise/-/yauzl-promise-4.0.0.tgz#4870124bfdbe4e7e23da4258719bb264fc18576a" + integrity sha512-/HCXpyHXJQQHvFq9noqrjfa/WpQC2XYs3vI7tBiAi4QiIU1knvYhZGaO1QPjwIVMdqflxbmwgMXtYeaRiAE0CA== + dependencies: + "@node-rs/crc32" "^1.7.0" + is-it-type "^5.1.2" + simple-invariant "^2.0.1" + +yauzl@^2.3.1: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yazl@^2.2.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35" + integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== + dependencies: + buffer-crc32 "~0.2.3" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/src/common.ts b/src/common.ts new file mode 100644 index 0000000..1536c65 --- /dev/null +++ b/src/common.ts @@ -0,0 +1,15 @@ +import { exec } from "child_process"; +import { promisify } from "util"; +import { window } from "vscode"; + +export interface RubyInterface { + error: boolean; + versionManager: { identifier: string }; + rubyVersion?: string; +} + +export const asyncExec = promisify(exec); +export const EXTENSION_NAME = "Ruby Environments"; +export const LOG_CHANNEL = window.createOutputChannel(EXTENSION_NAME, { + log: true, +}); diff --git a/src/extension.ts b/src/extension.ts index 36d6408..87ea55c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,11 +1,123 @@ -import * as vscode from "vscode"; +import { platform } from "os"; + +import { ManagerConfiguration, Ruby } from "./ruby"; +import { commands, env, ExtensionContext, ExtensionMode, TelemetrySender, WorkspaceFolder } from "vscode"; +import { LOG_CHANNEL } from "./common"; +import { WorkspaceChannel } from "./workspaceChannel"; + +export interface RubyEnvironment { + env: NodeJS.ProcessEnv; + rubyVersion?: string; + yjitEnabled?: boolean; + gemPath: string[]; + versionManager: ManagerConfiguration; +} // The public API that gets exposed to other extensions that depend on Ruby environments -// eslint-disable-next-line @typescript-eslint/no-empty-object-type -interface RubyEnvironmentsApi {} +export interface RubyEnvironmentsApi { + getEnvironment(workspaceFolder: WorkspaceFolder): Promise; + updateEnvironment(env: NodeJS.ProcessEnv, workspaceFolder: WorkspaceFolder): Promise; + refreshEnvironment(workspaceFolder: WorkspaceFolder): Promise; +} + +const workspaceRubyMap = new Map(); + +export async function activate(context: ExtensionContext): Promise { + const logger = await createLogger(context); + return { + getEnvironment: async (workspaceFolder: WorkspaceFolder) => { + let ruby = workspaceRubyMap.get(workspaceFolder); + if (!ruby) { + ruby = new Ruby(context, workspaceFolder, new WorkspaceChannel(workspaceFolder.name, LOG_CHANNEL), logger); + await ruby.activateRuby(); + workspaceRubyMap.set(workspaceFolder, ruby); + } + return { + env: ruby.env, + rubyVersion: ruby.rubyVersion, + yjitEnabled: ruby.yjitEnabled, + gemPath: ruby.gemPath, + versionManager: ruby.versionManager, + }; + }, + updateEnvironment: (env: NodeJS.ProcessEnv, workspaceFolder: WorkspaceFolder) => { + const ruby = workspaceRubyMap.get(workspaceFolder); + if (ruby) { + ruby.env = env; + } + return Promise.resolve(); + }, + refreshEnvironment: async (workspaceFolder: WorkspaceFolder) => { + const ruby = workspaceRubyMap.get(workspaceFolder); + if (ruby) { + await ruby.activateRuby(); + } + }, + }; +} -export function activate(_context: vscode.ExtensionContext): RubyEnvironmentsApi { - return {}; +export function deactivate() { + workspaceRubyMap.clear(); } -export function deactivate() {} +async function createLogger(context: ExtensionContext) { + let sender; + + switch (context.extensionMode) { + case ExtensionMode.Development: + sender = { + sendEventData: (eventName: string, data?: Record) => { + LOG_CHANNEL.debug(eventName, data); + }, + sendErrorData: (error: Error, data?: Record) => { + LOG_CHANNEL.error(error, data); + }, + }; + break; + case ExtensionMode.Test: + sender = { + sendEventData: (_eventName: string, _data?: Record) => {}, + sendErrorData: (_error: Error, _data?: Record) => {}, + }; + break; + default: + try { + let counter = 0; + + // If the extension that implements the getTelemetrySenderObject is not activated yet, the first invocation to + // the command will activate it, but it might actually return `null` rather than the sender object. Here we try + // a few times to receive a non `null` object back because we know that the getTelemetrySenderObject command + // exists (otherwise, we end up in the catch clause) + while (!sender && counter < 5) { + await commands.executeCommand("getTelemetrySenderObject"); + + sender = await commands.executeCommand("getTelemetrySenderObject"); + + counter++; + } + } catch (_error: any) { + sender = { + sendEventData: (_eventName: string, _data?: Record) => {}, + sendErrorData: (_error: Error, _data?: Record) => {}, + }; + } + break; + } + + if (!sender) { + sender = { + sendEventData: (_eventName: string, _data?: Record) => {}, + sendErrorData: (_error: Error, _data?: Record) => {}, + }; + } + + return env.createTelemetryLogger(sender, { + ignoreBuiltInCommonProperties: true, + ignoreUnhandledErrors: true, + additionalCommonProperties: { + extensionVersion: context.extension.packageJSON.version, + environment: platform(), + machineId: env.machineId, + }, + }); +} diff --git a/src/ruby.ts b/src/ruby.ts new file mode 100644 index 0000000..48ed4b7 --- /dev/null +++ b/src/ruby.ts @@ -0,0 +1,445 @@ +import path from "path"; +import os from "os"; + +import * as vscode from "vscode"; + +import { asyncExec, RubyInterface } from "./common"; +import { WorkspaceChannel } from "./workspaceChannel"; +import { Shadowenv, UntrustedWorkspaceError } from "./ruby/shadowenv"; +import { Chruby } from "./ruby/chruby"; +import { VersionManager } from "./ruby/versionManager"; +import { Mise } from "./ruby/mise"; +import { RubyInstaller } from "./ruby/rubyInstaller"; +import { Rbenv } from "./ruby/rbenv"; +import { Rvm } from "./ruby/rvm"; +import { None } from "./ruby/none"; +import { Custom } from "./ruby/custom"; +import { Asdf } from "./ruby/asdf"; + +async function detectMise() { + const possiblePaths = [ + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin", "mise"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "bin", "mise"), + ]; + + for (const possiblePath of possiblePaths) { + try { + await vscode.workspace.fs.stat(possiblePath); + return true; + } catch (_error: any) { + // Continue looking + } + } + + return false; +} + +export enum ManagerIdentifier { + Asdf = "asdf", + Auto = "auto", + Chruby = "chruby", + Rbenv = "rbenv", + Rvm = "rvm", + Shadowenv = "shadowenv", + Mise = "mise", + RubyInstaller = "rubyInstaller", + None = "none", + Custom = "custom", +} + +export interface ManagerConfiguration { + identifier: ManagerIdentifier; +} + +export class Ruby implements RubyInterface { + public rubyVersion?: string; + // This property indicates that Ruby has been compiled with YJIT support and that we're running on a Ruby version + // where it will be activated, either by the extension or by the server + public yjitEnabled?: boolean; + readonly gemPath: string[] = []; + private readonly workspaceFolder: vscode.WorkspaceFolder; + #versionManager: ManagerConfiguration = vscode.workspace + .getConfiguration("rubyLsp") + .get("rubyVersionManager") ?? { identifier: ManagerIdentifier.Auto }; + + private readonly shell = process.env.SHELL?.replace(/(\s+)/g, "\\$1"); + private _env: NodeJS.ProcessEnv = {}; + private _error = false; + private readonly context: vscode.ExtensionContext; + private readonly customBundleGemfile?: string; + private readonly outputChannel: WorkspaceChannel; + private readonly telemetry: vscode.TelemetryLogger; + + constructor( + context: vscode.ExtensionContext, + workspaceFolder: vscode.WorkspaceFolder, + outputChannel: WorkspaceChannel, + telemetry: vscode.TelemetryLogger, + ) { + this.context = context; + this.workspaceFolder = workspaceFolder; + this.outputChannel = outputChannel; + this.telemetry = telemetry; + + const customBundleGemfile = vscode.workspace.getConfiguration("rubyLsp").get("bundleGemfile"); + + if (customBundleGemfile && customBundleGemfile.length > 0) { + this.customBundleGemfile = path.isAbsolute(customBundleGemfile) + ? customBundleGemfile + : path.resolve(path.join(this.workspaceFolder.uri.fsPath, customBundleGemfile)); + } + } + + get versionManager(): ManagerConfiguration { + return this.#versionManager; + } + + private set versionManager(versionManager: ManagerConfiguration | ManagerIdentifier) { + if (typeof versionManager === "string") { + this.#versionManager.identifier = versionManager; + } else { + this.#versionManager = versionManager; + } + } + + set env(env: NodeJS.ProcessEnv) { + this._env = env; + } + + get env() { + return this._env; + } + + get error() { + return this._error; + } + + async activateRuby( + versionManager: ManagerConfiguration = vscode.workspace + .getConfiguration("rubyLsp") + .get("rubyVersionManager") ?? { identifier: ManagerIdentifier.Auto }, + ) { + this.versionManager = versionManager; + this._error = false; + + const workspaceRubyPath = await this.cachedWorkspaceRubyPath(); + + if (workspaceRubyPath) { + // If a workspace specific Ruby path is configured, then we use that to activate the environment + await this.runActivation( + new None( + this.workspaceFolder, + this.outputChannel, + this.context, + this.manuallySelectRuby.bind(this), + workspaceRubyPath, + ), + ); + } else { + // If the version manager is auto, discover the actual manager before trying to activate anything + if (this.versionManager.identifier === ManagerIdentifier.Auto) { + await this.discoverVersionManager(); + this.outputChannel.info(`Discovered version manager ${this.versionManager.identifier}`); + } + + try { + await this.runManagerActivation(); + } catch (error: any) { + if (!(error instanceof UntrustedWorkspaceError)) { + this.telemetry.logError(error, { + appType: "extension", + appVersion: this.context.extension.packageJSON.version, + versionManager: this.versionManager.identifier, + workspace: new vscode.TelemetryTrustedValue(this.workspaceFolder.name), + }); + } + + // If an error occurred and a global Ruby path is configured, then we can try to fallback to that + const globalRubyPath = vscode.workspace + .getConfiguration("rubyLsp") + .get("rubyExecutablePath"); + + if (globalRubyPath) { + await this.runActivation( + new None( + this.workspaceFolder, + this.outputChannel, + this.context, + this.manuallySelectRuby.bind(this), + globalRubyPath, + ), + ); + } else { + this._error = true; + + // When running tests, we need to throw the error or else activation may silently fail and it's very difficult + // to debug + if (this.context.extensionMode === vscode.ExtensionMode.Test) { + throw error; + } + + await this.handleRubyError(error.message); + } + } + } + + if (!this.error) { + this.fetchRubyVersionInfo(); + await this.setupBundlePath(); + } + } + + async manuallySelectRuby() { + const manualSelection = await vscode.window.showInformationMessage( + "Configure global or workspace specific fallback for the Ruby LSP?", + "global", + "workspace", + "clear previous workspace selection", + ); + + if (!manualSelection) { + return; + } + + if (manualSelection === "clear previous workspace selection") { + await this.context.workspaceState.update(`rubyLsp.workspaceRubyPath.${this.workspaceFolder.name}`, undefined); + return this.activateRuby(); + } + + const selection = await vscode.window.showOpenDialog({ + title: `Select Ruby binary path for ${manualSelection} configuration`, + openLabel: "Select Ruby binary", + canSelectMany: false, + }); + + if (!selection) { + return; + } + + const selectedPath = selection[0].fsPath; + + if (manualSelection === "global") { + await vscode.workspace.getConfiguration("rubyLsp").update("rubyExecutablePath", selectedPath, true); + } else { + // We must update the cached Ruby path for this workspace if the user decided to change it + await this.context.workspaceState.update(`rubyLsp.workspaceRubyPath.${this.workspaceFolder.name}`, selectedPath); + } + + return this.activateRuby(); + } + + private async runActivation(manager: VersionManager) { + const { env, version, yjit, gemPath } = await manager.activate(); + const [major, minor, _patch] = version.split(".").map(Number); + + this.sanitizeEnvironment(env); + + this._env = env; + this.rubyVersion = version; + this.yjitEnabled = (yjit && major > 3) || (major === 3 && minor >= 2); + this.gemPath.push(...gemPath); + } + + // Fetch information related to the Ruby version. This can only be invoked after activation, so that `rubyVersion` is + // set + private fetchRubyVersionInfo() { + const [major, minor, _patch] = this.rubyVersion!.split(".").map(Number); + + if (major < 3) { + throw new Error( + `The Ruby LSP requires Ruby 3.0 or newer to run. This project is using ${this.rubyVersion}. \ + [See alternatives](https://github.com/Shopify/ruby-lsp/blob/main/vscode/README.md#ruby-version-requirement)`, + ); + } + + // Starting with Ruby 3.3 the server enables YJIT itself + if (this.yjitEnabled && major === 3 && minor === 2) { + // RUBYOPT may be empty or it may contain bundler paths. In the second case, we must concat to avoid accidentally + // removing the paths from the env variable + if (this._env.RUBYOPT) { + this._env.RUBYOPT.concat(" --yjit"); + } else { + this._env.RUBYOPT = "--yjit"; + } + } + } + + // Deletes environment variables that are known to cause issues for launching the Ruby LSP. For example, GC tuning + // variables or verbose settings + private sanitizeEnvironment(env: NodeJS.ProcessEnv) { + // Delete all GC tuning variables + Object.keys(env).forEach((key) => { + if (key.startsWith("RUBY_GC")) { + delete env[key]; + } + }); + + // Delete verbose or debug related settings. These often make Bundler or other dependencies print things to STDOUT, + // which breaks the client/server communication + delete env.VERBOSE; + delete env.DEBUG; + } + + private async runManagerActivation() { + switch (this.versionManager.identifier) { + case ManagerIdentifier.Asdf: + await this.runActivation( + new Asdf(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.Chruby: + await this.runActivation( + new Chruby(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.Rbenv: + await this.runActivation( + new Rbenv(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.Rvm: + await this.runActivation( + new Rvm(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.Mise: + await this.runActivation( + new Mise(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.RubyInstaller: + await this.runActivation( + new RubyInstaller(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.Custom: + await this.runActivation( + new Custom(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + case ManagerIdentifier.None: + await this.runActivation( + new None(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + default: + await this.runActivation( + new Shadowenv(this.workspaceFolder, this.outputChannel, this.context, this.manuallySelectRuby.bind(this)), + ); + break; + } + } + + private async setupBundlePath() { + // Some users like to define a completely separate Gemfile for development tools. We allow them to use + // `rubyLsp.bundleGemfile` to configure that and need to inject it into the environment + if (!this.customBundleGemfile) { + return; + } + + try { + await vscode.workspace.fs.stat(vscode.Uri.file(this.customBundleGemfile)); + this._env.BUNDLE_GEMFILE = this.customBundleGemfile; + } catch (_error: any) { + throw new Error(`The configured bundle gemfile ${this.customBundleGemfile} does not exist`); + } + } + + private async discoverVersionManager() { + // For shadowenv, it wouldn't be enough to check for the executable's existence. We need to check if the project has + // created a .shadowenv.d folder + try { + await vscode.workspace.fs.stat(vscode.Uri.joinPath(this.workspaceFolder.uri, ".shadowenv.d")); + this.versionManager.identifier = ManagerIdentifier.Shadowenv; + return; + } catch (_error: any) { + // If .shadowenv.d doesn't exist, then we check the other version managers + } + + const managers = [ManagerIdentifier.Chruby, ManagerIdentifier.Rbenv, ManagerIdentifier.Rvm, ManagerIdentifier.Asdf]; + + for (const tool of managers) { + const exists = await this.toolExists(tool); + + if (exists) { + this.versionManager = tool; + return; + } + } + + if (await detectMise()) { + this.versionManager = ManagerIdentifier.Mise; + return; + } + + if (os.platform() === "win32") { + this.versionManager = ManagerIdentifier.RubyInstaller; + return; + } + + // If we can't find a version manager, just return None + this.versionManager = ManagerIdentifier.None; + } + + private async toolExists(tool: string) { + try { + let command = this.shell ? `${this.shell} -i -c '` : ""; + command += `${tool} --version`; + + if (this.shell) { + command += "'"; + } + + this.outputChannel.info(`Checking if ${tool} is available on the path with command: ${command}`); + + await asyncExec(command, { + cwd: this.workspaceFolder.uri.fsPath, + timeout: 1000, + }); + return true; + } catch { + return false; + } + } + + private async handleRubyError(message: string) { + const answer = await vscode.window.showErrorMessage( + `Automatic Ruby environment activation with ${this.versionManager.identifier} failed: ${message}`, + "Retry", + "Select Ruby manually", + ); + + // If the user doesn't answer anything, we can just return. The error property was already set to true and we won't + // try to launch the LSP + if (!answer) { + return; + } + + // For retrying, reload the entire window to get rid of any state + if (answer === "Retry") { + await vscode.commands.executeCommand("workbench.action.reloadWindow"); + } + + return this.manuallySelectRuby(); + } + + private async cachedWorkspaceRubyPath() { + const workspaceRubyPath = this.context.workspaceState.get( + `rubyLsp.workspaceRubyPath.${this.workspaceFolder.name}`, + ); + + if (!workspaceRubyPath) { + return undefined; + } + + try { + await vscode.workspace.fs.stat(vscode.Uri.file(workspaceRubyPath)); + return workspaceRubyPath; + } catch (_error: any) { + // If the user selected a Ruby path and then uninstalled it, we need to clear the the cached path + this.context.workspaceState.update(`rubyLsp.workspaceRubyPath.${this.workspaceFolder.name}`, undefined); + return undefined; + } + } +} diff --git a/src/ruby/asdf.ts b/src/ruby/asdf.ts new file mode 100644 index 0000000..e06ff5a --- /dev/null +++ b/src/ruby/asdf.ts @@ -0,0 +1,88 @@ +import os from "os"; +import path from "path"; + +import * as vscode from "vscode"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// A tool to manage multiple runtime versions with a single CLI tool +// +// Learn more: https://github.com/asdf-vm/asdf +export class Asdf extends VersionManager { + async activate(): Promise { + // These directories are where we can find the ASDF executable for v0.16 and above + const possibleExecutablePaths = [ + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "local", "bin"), + ]; + + // Prefer the path configured by the user, then the ASDF scripts for versions below v0.16 and finally the + // executables for v0.16 and above + const asdfPath = + (await this.getConfiguredAsdfPath()) ?? + (await this.findAsdfInstallation()) ?? + (await this.findExec(possibleExecutablePaths, "asdf")); + + // If there's no extension name, then we are using the ASDF executable directly. If there is an extension, then it's + // a shell script and we have to source it first + const baseCommand = path.extname(asdfPath) === "" ? asdfPath : `. ${asdfPath} && asdf`; + + const parsedResult = await this.runEnvActivationScript(`${baseCommand} exec ruby`); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } + + // Only public for testing. Finds the ASDF installation URI based on what's advertised in the ASDF documentation + async findAsdfInstallation(): Promise { + const scriptName = path.basename(vscode.env.shell) === "fish" ? "asdf.fish" : "asdf.sh"; + + // Possible ASDF installation paths as described in https://asdf-vm.com/guide/getting-started.html#_3-install-asdf. + // In order, the methods of installation are: + // 1. Git + // 2. Pacman + // 3. Homebrew M series + // 4. Homebrew Intel series + const possiblePaths = [ + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".asdf", scriptName), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "asdf-vm", scriptName), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "opt", "asdf", "libexec", scriptName), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "local", "opt", "asdf", "libexec", scriptName), + ]; + + for (const possiblePath of possiblePaths) { + try { + await vscode.workspace.fs.stat(possiblePath); + return possiblePath.fsPath; + } catch (_error: any) { + // Continue looking + } + } + + this.outputChannel.info(`Could not find installation for ASDF < v0.16. Searched in ${possiblePaths.join(", ")}`); + return undefined; + } + + private async getConfiguredAsdfPath(): Promise { + const config = vscode.workspace.getConfiguration("rubyLsp"); + const asdfPath = config.get("rubyVersionManager.asdfExecutablePath"); + + if (!asdfPath) { + return; + } + + const configuredPath = vscode.Uri.file(asdfPath); + + try { + await vscode.workspace.fs.stat(configuredPath); + this.outputChannel.info(`Using configured ASDF executable path: ${asdfPath}`); + return configuredPath.fsPath; + } catch (_error: any) { + throw new Error(`ASDF executable configured as ${configuredPath.fsPath}, but that file doesn't exist`); + } + } +} diff --git a/src/ruby/chruby.ts b/src/ruby/chruby.ts new file mode 100644 index 0000000..0036efb --- /dev/null +++ b/src/ruby/chruby.ts @@ -0,0 +1,440 @@ +import os from "os"; +import path from "path"; + +import * as vscode from "vscode"; + +import { WorkspaceChannel } from "../workspaceChannel"; + +import { ActivationResult, VersionManager, ACTIVATION_SEPARATOR } from "./versionManager"; + +interface RubyVersion { + engine?: string; + version: string; +} + +class RubyActivationCancellationError extends Error {} + +// A tool to change the current Ruby version +// Learn more: https://github.com/postmodern/chruby +export class Chruby extends VersionManager { + // Only public so that we can point to a different directory in tests + public rubyInstallationUris = [ + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".rubies"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "rubies"), + ]; + + constructor( + workspaceFolder: vscode.WorkspaceFolder, + outputChannel: WorkspaceChannel, + context: vscode.ExtensionContext, + manuallySelectRuby: () => Promise, + ) { + super(workspaceFolder, outputChannel, context, manuallySelectRuby); + + const configuredRubies = vscode.workspace + .getConfiguration("rubyLsp") + .get("rubyVersionManager.chrubyRubies"); + + if (configuredRubies) { + this.rubyInstallationUris.push(...configuredRubies.map((path) => vscode.Uri.file(path))); + } + } + + async activate(): Promise { + let versionInfo = await this.discoverRubyVersion(); + let rubyUri: vscode.Uri | undefined; + + // If the version informed is available, try to find the Ruby installation. Otherwise, try to fall back to an + // existing version + try { + if (versionInfo) { + rubyUri = await this.findRubyUri(versionInfo); + } else { + const fallback = await this.fallbackWithCancellation( + "No .ruby-version file found. Trying to fall back to latest installed Ruby in 10 seconds", + "You can create a .ruby-version file in a parent directory to configure a fallback", + this.findFallbackRuby.bind(this), + this.rubyVersionError.bind(this), + ); + + versionInfo = fallback.rubyVersion; + rubyUri = fallback.uri; + } + } catch (error: any) { + if (error instanceof RubyActivationCancellationError) { + // Try to re-activate if the user has configured a fallback during cancellation + return this.activate(); + } + + throw error; + } + + // If we couldn't find a Ruby installation, that means there's a `.ruby-version` file, but that Ruby is not + // installed. In this case, we fallback to a closest installation of Ruby - preferably only varying in patch + try { + if (!rubyUri) { + const currentVersionInfo = { ...versionInfo }; + + const fallback = await this.fallbackWithCancellation( + `Couldn't find installation for ${versionInfo.version}. Trying to fall back to other Ruby in 10 seconds`, + "You can cancel this fallback and install the required Ruby", + async () => this.findClosestRubyInstallation(currentVersionInfo), + () => this.missingRubyError(currentVersionInfo.version), + ); + + versionInfo = fallback.rubyVersion; + rubyUri = fallback.uri; + } + } catch (error: any) { + if (error instanceof RubyActivationCancellationError) { + // Try to re-activate if the user has configured a fallback during cancellation + return this.activate(); + } + + throw error; + } + + this.outputChannel.info(`Discovered Ruby installation at ${rubyUri.fsPath}`); + + const { defaultGems, gemHome, yjit, version } = await this.runActivationScript(rubyUri, versionInfo); + + this.outputChannel.info(`Activated Ruby environment: defaultGems=${defaultGems} gemHome=${gemHome} yjit=${yjit}`); + + const rubyEnv = { + GEM_HOME: gemHome, + GEM_PATH: `${gemHome}${path.delimiter}${defaultGems}`, + PATH: `${path.join(gemHome, "bin")}${path.delimiter}${path.join( + defaultGems, + "bin", + )}${path.delimiter}${path.dirname(rubyUri.fsPath)}${path.delimiter}${this.getProcessPath()}`, + }; + + return { + env: { ...process.env, ...rubyEnv }, + yjit, + version, + gemPath: [gemHome, defaultGems], + }; + } + + protected getProcessPath() { + return process.env.PATH; + } + + // Returns the full URI to the Ruby executable + protected async findRubyUri(rubyVersion: RubyVersion): Promise { + const possibleVersionNames = rubyVersion.engine + ? [`${rubyVersion.engine}-${rubyVersion.version}`, rubyVersion.version] + : [rubyVersion.version, `ruby-${rubyVersion.version}`]; + + for (const uri of this.rubyInstallationUris) { + let directories; + + try { + directories = (await vscode.workspace.fs.readDirectory(uri)).sort((left, right) => + right[0].localeCompare(left[0]), + ); + } catch (_error: any) { + // If the directory doesn't exist, keep searching + this.outputChannel.debug(`Tried searching for Ruby installation in ${uri.fsPath} but it doesn't exist`); + continue; + } + + for (const versionName of possibleVersionNames) { + const targetDirectory = directories.find(([name]) => name.startsWith(versionName)); + + if (targetDirectory) { + return vscode.Uri.joinPath(uri, targetDirectory[0], "bin", "ruby"); + } + } + } + + return undefined; + } + + // Run the activation script using the Ruby installation we found so that we can discover gem paths + protected async runActivationScript( + rubyExecutableUri: vscode.Uri, + rubyVersion: RubyVersion, + ): Promise<{ + defaultGems: string; + gemHome: string; + yjit: boolean; + version: string; + }> { + const activationUri = vscode.Uri.joinPath(this.context.extensionUri, "chruby_activation.rb"); + + const result = await this.runScript( + `${rubyExecutableUri.fsPath} -EUTF-8:UTF-8 '${activationUri.fsPath}' ${rubyVersion.version}`, + ); + + const [defaultGems, gemHome, yjit, version] = result.stderr.split(ACTIVATION_SEPARATOR); + + return { defaultGems, gemHome, yjit: yjit === "true", version }; + } + + private async findClosestRubyInstallation(rubyVersion: RubyVersion): Promise<{ + uri: vscode.Uri; + rubyVersion: RubyVersion; + }> { + const [major, minor, _patch] = rubyVersion.version.split("."); + const directories: { uri: vscode.Uri; rubyVersion: RubyVersion }[] = []; + + for (const uri of this.rubyInstallationUris) { + try { + // Accumulate all directories that match the `engine-version` pattern and that start with the same requested + // major version. We do not try to approximate major versions + (await vscode.workspace.fs.readDirectory(uri)).forEach(([name]) => { + const match = /((?[A-Za-z]+)-)?(?\d+\.\d+(\.\d+)?(-[A-Za-z0-9]+)?)/.exec(name); + + if (match?.groups && match.groups.version.startsWith(major)) { + directories.push({ + uri: vscode.Uri.joinPath(uri, name, "bin", "ruby"), + rubyVersion: { + engine: match.groups.engine, + version: match.groups.version, + }, + }); + } + }); + } catch (_error: any) { + // If the directory doesn't exist, keep searching + this.outputChannel.debug(`Tried searching for Ruby installation in ${uri.fsPath} but it doesn't exist`); + continue; + } + } + + // Sort the directories based on the difference between the minor version and the requested minor version. On + // conflicts, we use the patch version to break the tie. If there's no distance, we prefer the higher patch version + const closest = directories.sort((left, right) => { + const leftVersion = left.rubyVersion.version.split("."); + const rightVersion = right.rubyVersion.version.split("."); + + const leftDiff = Math.abs(Number(leftVersion[1]) - Number(minor)); + const rightDiff = Math.abs(Number(rightVersion[1]) - Number(minor)); + + // If the distance to minor version is the same, prefer higher patch number + if (leftDiff === rightDiff) { + return Number(rightVersion[2] || 0) - Number(leftVersion[2] || 0); + } + + return leftDiff - rightDiff; + })[0]; + + if (closest) { + return closest; + } + + throw new Error("Cannot find any Ruby installations"); + } + + // Returns the Ruby version information including version and engine. E.g.: ruby-3.3.0, truffleruby-21.3.0 + private async discoverRubyVersion(): Promise { + let uri = this.bundleUri; + const root = path.parse(uri.fsPath).root; + let version: string; + let rubyVersionUri: vscode.Uri; + + while (uri.fsPath !== root) { + try { + rubyVersionUri = vscode.Uri.joinPath(uri, ".ruby-version"); + const content = await vscode.workspace.fs.readFile(rubyVersionUri); + version = content.toString().trim(); + } catch (_error: any) { + // If the file doesn't exist, continue going up the directory tree + uri = vscode.Uri.file(path.dirname(uri.fsPath)); + continue; + } + + if (version === "") { + throw new Error(`Ruby version file ${rubyVersionUri.fsPath} is empty`); + } + + const match = /((?[A-Za-z]+)-)?(?\d+\.\d+(\.\d+)?(-[A-Za-z0-9]+)?)/.exec(version); + + if (!match?.groups) { + throw new Error( + `Ruby version file ${rubyVersionUri.fsPath} contains invalid format. Expected (engine-)?version, got ${version}`, + ); + } + + this.outputChannel.info(`Discovered Ruby version ${version} from ${rubyVersionUri.fsPath}`); + return { engine: match.groups.engine, version: match.groups.version }; + } + + return undefined; + } + + private async fallbackWithCancellation( + title: string, + message: string, + fallbackFn: () => Promise, + errorFn: () => Error, + ): Promise { + let gemfileContents; + + try { + gemfileContents = await vscode.workspace.fs.readFile(vscode.Uri.joinPath(this.workspaceFolder.uri, "Gemfile")); + } catch (_error: any) { + // The Gemfile doesn't exist + } + + // If the Gemfile includes ruby version restrictions, then trying to fall back may lead to errors + if (gemfileContents && /^ruby(\s|\()("|')[\d.]+/.test(gemfileContents.toString())) { + throw errorFn(); + } + + const fallback = await vscode.window.withProgress( + { + title, + location: vscode.ProgressLocation.Notification, + cancellable: true, + }, + async (progress, token) => { + progress.report({ message }); + + // If they don't cancel, we wait 10 seconds before falling back so that they are aware of what's happening + await new Promise((resolve) => { + setTimeout(resolve, 10000); + + // If the user cancels the fallback, resolve immediately so that they don't have to wait 10 seconds + token.onCancellationRequested(() => { + resolve(); + }); + }); + + if (token.isCancellationRequested) { + await this.handleCancelledFallback(errorFn); + + // We throw this error to be able to catch and re-run activation after the user has configured a fallback + throw new RubyActivationCancellationError(); + } + + return fallbackFn(); + }, + ); + + return fallback; + } + + private async handleCancelledFallback(errorFn: () => Error) { + const answer = await vscode.window.showInformationMessage( + `The Ruby LSP requires a Ruby version to launch. + You can define a fallback for the system or for the Ruby LSP only`, + "System", + "Ruby LSP only", + ); + + if (answer === "System") { + await this.createParentRubyVersionFile(errorFn); + } else if (answer === "Ruby LSP only") { + await this.manuallySelectRuby(); + } + + throw errorFn(); + } + + private async createParentRubyVersionFile(errorFn: () => Error) { + const items: vscode.QuickPickItem[] = []; + + for (const uri of this.rubyInstallationUris) { + let directories; + + try { + directories = (await vscode.workspace.fs.readDirectory(uri)).sort((left, right) => + right[0].localeCompare(left[0]), + ); + + directories.forEach((directory) => { + items.push({ + label: directory[0], + }); + }); + } catch (_error: any) { + continue; + } + } + + const answer = await vscode.window.showQuickPick(items, { + title: "Select a Ruby version to use as fallback", + ignoreFocusOut: true, + }); + + if (!answer) { + throw errorFn(); + } + + const targetDirectory = await vscode.window.showOpenDialog({ + defaultUri: vscode.Uri.file(os.homedir()), + openLabel: "Add fallback in this directory", + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + title: "Select the directory to create the .ruby-version fallback in", + }); + + if (!targetDirectory) { + throw errorFn(); + } + + await vscode.workspace.fs.writeFile( + vscode.Uri.joinPath(targetDirectory[0], ".ruby-version"), + Buffer.from(answer.label), + ); + } + + private async findFallbackRuby(): Promise<{ + uri: vscode.Uri; + rubyVersion: RubyVersion; + }> { + for (const uri of this.rubyInstallationUris) { + let directories; + + try { + directories = (await vscode.workspace.fs.readDirectory(uri)).sort((left, right) => + right[0].localeCompare(left[0]), + ); + + let groups; + let targetDirectory; + + for (const directory of directories) { + const match = /((?[A-Za-z]+)-)?(?\d+\.\d+(\.\d+)?(-[A-Za-z0-9]+)?)/.exec(directory[0]); + + if (match?.groups) { + groups = match.groups; + targetDirectory = directory; + break; + } + } + + if (targetDirectory && groups) { + return { + uri: vscode.Uri.joinPath(uri, targetDirectory[0], "bin", "ruby"), + rubyVersion: { + engine: groups.engine, + version: groups.version, + }, + }; + } + } catch (_error: any) { + // If the directory doesn't exist, keep searching + this.outputChannel.debug(`Tried searching for Ruby installation in ${uri.fsPath} but it doesn't exist`); + continue; + } + } + + throw new Error("Cannot find any Ruby installations"); + } + + private missingRubyError(version: string) { + return new Error(`Cannot find Ruby installation for version ${version}`); + } + + private rubyVersionError() { + return new Error( + `Cannot find .ruby-version file. Please specify the Ruby version in a + .ruby-version either in ${this.bundleUri.fsPath} or in a parent directory`, + ); + } +} diff --git a/src/ruby/custom.ts b/src/ruby/custom.ts new file mode 100644 index 0000000..c4564c7 --- /dev/null +++ b/src/ruby/custom.ts @@ -0,0 +1,35 @@ +import * as vscode from "vscode"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// Custom +// +// Custom Ruby environment activation can be used for all cases where an existing version manager does not suffice. +// Users are allowed to define a shell script that runs before calling ruby, giving them the chance to modify the PATH, +// GEM_HOME and GEM_PATH as needed to find the correct Ruby runtime. +export class Custom extends VersionManager { + async activate(): Promise { + const parsedResult = await this.runEnvActivationScript(`${this.customCommand()} && ruby`); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } + + customCommand() { + const configuration = vscode.workspace.getConfiguration("rubyLsp"); + const customCommand: string | undefined = configuration.get("customRubyCommand"); + + if (customCommand === undefined) { + throw new Error( + "The customRubyCommand configuration must be set when 'custom' is selected as the version manager. \ + See the [README](https://shopify.github.io/ruby-lsp/version-managers.html) for instructions.", + ); + } + + return customCommand; + } +} diff --git a/src/ruby/mise.ts b/src/ruby/mise.ts new file mode 100644 index 0000000..2c647ed --- /dev/null +++ b/src/ruby/mise.ts @@ -0,0 +1,65 @@ +import os from "os"; + +import * as vscode from "vscode"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// Mise (mise en place) is a manager for dev tools, environment variables and tasks +// +// Learn more: https://github.com/jdx/mise +export class Mise extends VersionManager { + async activate(): Promise { + const miseUri = await this.findMiseUri(); + + // The exec command in Mise is called `x` + const parsedResult = await this.runEnvActivationScript(`${miseUri.fsPath} x -- ruby`); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } + + async findMiseUri(): Promise { + const config = vscode.workspace.getConfiguration("rubyLsp"); + const misePath = config.get("rubyVersionManager.miseExecutablePath"); + + if (misePath) { + const configuredPath = vscode.Uri.file(misePath); + + try { + await vscode.workspace.fs.stat(configuredPath); + return configuredPath; + } catch (_error: any) { + throw new Error(`Mise executable configured as ${configuredPath.fsPath}, but that file doesn't exist`); + } + } + + // Possible mise installation paths + // + // 1. Installation from curl | sh (per mise.jdx.dev Getting Started) + // 2. Homebrew M series + // 3. Installation from `apt install mise` + const possiblePaths = [ + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "opt", "homebrew", "bin", "mise"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "bin", "mise"), + ]; + + for (const possiblePath of possiblePaths) { + try { + await vscode.workspace.fs.stat(possiblePath); + return possiblePath; + } catch (_error: any) { + // Continue looking + } + } + + throw new Error( + `The Ruby LSP version manager is configured to be Mise, but could not find Mise installation. Searched in + ${possiblePaths.join(", ")}`, + ); + } +} diff --git a/src/ruby/none.ts b/src/ruby/none.ts new file mode 100644 index 0000000..0c6a8c7 --- /dev/null +++ b/src/ruby/none.ts @@ -0,0 +1,39 @@ +import * as vscode from "vscode"; + +import { WorkspaceChannel } from "../workspaceChannel"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// None +// +// This "version manager" represents the case where no manager is used, but the environment still needs to be inserted +// into the NodeJS process. For example, when you use Docker, install Ruby through Homebrew or use some other mechanism +// to have Ruby available in your PATH automatically. +// +// If you don't have Ruby automatically available in your PATH and are not using a version manager, look into +// configuring custom Ruby activation +export class None extends VersionManager { + private readonly rubyPath: string; + + constructor( + workspaceFolder: vscode.WorkspaceFolder, + outputChannel: WorkspaceChannel, + context: vscode.ExtensionContext, + manuallySelectRuby: () => Promise, + rubyPath?: string, + ) { + super(workspaceFolder, outputChannel, context, manuallySelectRuby); + this.rubyPath = rubyPath ?? "ruby"; + } + + async activate(): Promise { + const parsedResult = await this.runEnvActivationScript(this.rubyPath); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } +} diff --git a/src/ruby/rbenv.ts b/src/ruby/rbenv.ts new file mode 100644 index 0000000..4c7308c --- /dev/null +++ b/src/ruby/rbenv.ts @@ -0,0 +1,42 @@ +import * as vscode from "vscode"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// Seamlessly manage your app’s Ruby environment with rbenv. +// +// Learn more: https://github.com/rbenv/rbenv +export class Rbenv extends VersionManager { + async activate(): Promise { + const rbenvExec = await this.findRbenv(); + + const parsedResult = await this.runEnvActivationScript(`${rbenvExec} exec ruby`); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } + + private async findRbenv(): Promise { + const config = vscode.workspace.getConfiguration("rubyLsp"); + const configuredRbenvPath = config.get("rubyVersionManager.rbenvExecutablePath"); + + if (configuredRbenvPath) { + return this.ensureRbenvExistsAt(configuredRbenvPath); + } else { + return this.findExec([vscode.Uri.file("/opt/homebrew/bin"), vscode.Uri.file("/usr/local/bin")], "rbenv"); + } + } + + private async ensureRbenvExistsAt(path: string): Promise { + try { + await vscode.workspace.fs.stat(vscode.Uri.file(path)); + + return path; + } catch (_error: any) { + throw new Error(`The Ruby LSP version manager is configured to be rbenv, but ${path} does not exist`); + } + } +} diff --git a/src/ruby/rubyInstaller.ts b/src/ruby/rubyInstaller.ts new file mode 100644 index 0000000..3adc050 --- /dev/null +++ b/src/ruby/rubyInstaller.ts @@ -0,0 +1,65 @@ +import os from "os"; + +import * as vscode from "vscode"; + +import { Chruby } from "./chruby"; + +interface RubyVersion { + engine?: string; + version: string; +} + +// Most version managers do not support Windows. One popular way of installing Ruby on Windows is via RubyInstaller, +// which places the rubies in directories like C:\Ruby32-x64 (i.e.: Ruby{major}{minor}-{arch}). To automatically switch +// Ruby versions on Windows, we use the same mechanism as Chruby to discover the Ruby version based on `.ruby-version` +// files and then try to search the directories commonly used by RubyInstaller. +// +// If we can't find it there, then we throw an error and rely on the user to manually select where Ruby is installed. +export class RubyInstaller extends Chruby { + // Environment variables are case sensitive on Windows when we access them through NodeJS. We need to ensure that + // we're searching through common variations, so that we don't accidentally miss the path we should inherit + protected getProcessPath() { + return process.env.Path ?? process.env.PATH ?? process.env.path; + } + + // Returns the full URI to the Ruby executable + protected async findRubyUri(rubyVersion: RubyVersion): Promise { + const [major, minor, _patch] = rubyVersion.version.split(".").map(Number); + + const possibleInstallationUris = [ + vscode.Uri.joinPath(vscode.Uri.file("C:"), `Ruby${major}${minor}-${os.arch()}`), + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), `Ruby${major}${minor}-${os.arch()}`), + ]; + + for (const installationUri of possibleInstallationUris) { + try { + await vscode.workspace.fs.stat(installationUri); + return vscode.Uri.joinPath(installationUri, "bin", "ruby"); + } catch (_error: any) { + // Continue searching + } + } + + throw new Error( + `Cannot find installation directory for Ruby version ${rubyVersion.version}.\ + Searched in ${possibleInstallationUris.map((uri) => uri.fsPath).join(", ")}`, + ); + } + + protected async runActivationScript( + rubyExecutableUri: vscode.Uri, + rubyVersion: RubyVersion, + ): Promise<{ + defaultGems: string; + gemHome: string; + yjit: boolean; + version: string; + }> { + const activationResult = await super.runActivationScript(rubyExecutableUri, rubyVersion); + + activationResult.gemHome = activationResult.gemHome.replace(/\//g, "\\"); + activationResult.defaultGems = activationResult.defaultGems.replace(/\//g, "\\"); + + return activationResult; + } +} diff --git a/src/ruby/rvm.ts b/src/ruby/rvm.ts new file mode 100644 index 0000000..66f508d --- /dev/null +++ b/src/ruby/rvm.ts @@ -0,0 +1,50 @@ +import os from "os"; + +import * as vscode from "vscode"; + +import { ActivationResult, VersionManager } from "./versionManager"; + +// Ruby enVironment Manager. It manages Ruby application environments and enables switching between them. +// Learn more: +// - https://github.com/rvm/rvm +// - https://rvm.io +export class Rvm extends VersionManager { + async activate(): Promise { + const installationPath = await this.findRvmInstallation(); + const parsedResult = await this.runEnvActivationScript(installationPath.fsPath); + + const activatedKeys = Object.entries(parsedResult.env) + .map(([key, value]) => `${key}=${value}`) + .join(" "); + + this.outputChannel.info(`Activated Ruby environment: ${activatedKeys}`); + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } + + async findRvmInstallation(): Promise { + const possiblePaths = [ + vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".rvm", "bin", "rvm-auto-ruby"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "local", "rvm", "bin", "rvm-auto-ruby"), + vscode.Uri.joinPath(vscode.Uri.file("/"), "usr", "share", "rvm", "bin", "rvm-auto-ruby"), + ]; + + for (const uri of possiblePaths) { + try { + await vscode.workspace.fs.stat(uri); + return uri; + } catch (_error: any) { + // Continue to the next installation path + } + } + + throw new Error( + `Cannot find RVM installation directory. Searched in ${possiblePaths.map((uri) => uri.fsPath).join(",")}`, + ); + } +} diff --git a/src/ruby/shadowenv.ts b/src/ruby/shadowenv.ts new file mode 100644 index 0000000..a683ad8 --- /dev/null +++ b/src/ruby/shadowenv.ts @@ -0,0 +1,72 @@ +import * as vscode from "vscode"; + +import { asyncExec } from "../common"; + +import { VersionManager, ActivationResult } from "./versionManager"; + +// Shadowenv is a tool that allows managing environment variables upon entering a directory. It allows users to manage +// which Ruby version should be used for each project, in addition to other customizations such as GEM_HOME. +// +// Learn more: https://github.com/Shopify/shadowenv +export class UntrustedWorkspaceError extends Error {} + +export class Shadowenv extends VersionManager { + async activate(): Promise { + try { + await vscode.workspace.fs.stat(vscode.Uri.joinPath(this.bundleUri, ".shadowenv.d")); + } catch (_error: any) { + throw new Error( + "The Ruby LSP version manager is configured to be shadowenv, \ + but no .shadowenv.d directory was found in the workspace", + ); + } + + const shadowenvExec = await this.findExec([vscode.Uri.file("/opt/homebrew/bin")], "shadowenv"); + + try { + const parsedResult = await this.runEnvActivationScript(`${shadowenvExec} exec -- ruby`); + + // Do not let Shadowenv change the BUNDLE_GEMFILE. The server has to be able to control this in order to properly + // set up the environment + delete parsedResult.env.BUNDLE_GEMFILE; + + return { + env: { ...process.env, ...parsedResult.env }, + yjit: parsedResult.yjit, + version: parsedResult.version, + gemPath: parsedResult.gemPath, + }; + } catch (error: any) { + const err = error as Error; + // If the workspace is untrusted, offer to trust it for the user + if (err.message.includes("untrusted shadowenv program")) { + const answer = await vscode.window.showErrorMessage( + `Tried to activate Shadowenv, but the workspace is untrusted. + Workspaces must be trusted to before allowing Shadowenv to load the environment for security reasons.`, + "Trust workspace", + "Shutdown Ruby LSP", + ); + + if (answer === "Trust workspace") { + await asyncExec("shadowenv trust", { cwd: this.bundleUri.fsPath }); + return this.activate(); + } + + throw new UntrustedWorkspaceError("Cannot activate Ruby environment in an untrusted workspace"); + } + + try { + await asyncExec("shadowenv --version"); + } catch (_error: any) { + throw new Error( + `Shadowenv executable not found. Ensure it is installed and available in the PATH. + This error may happen if your shell configuration is failing to be sourced from the editor or if + another extension is mutating the process PATH.`, + ); + } + + // If it failed for some other reason, present the error to the user + throw new Error(`Failed to activate Ruby environment with Shadowenv: ${error.message}`); + } + } +} diff --git a/src/ruby/versionManager.ts b/src/ruby/versionManager.ts new file mode 100644 index 0000000..92fdcd2 --- /dev/null +++ b/src/ruby/versionManager.ts @@ -0,0 +1,111 @@ +import path from "path"; +import os from "os"; + +import * as vscode from "vscode"; + +import { WorkspaceChannel } from "../workspaceChannel"; +import { asyncExec } from "../common"; + +export interface ActivationResult { + env: NodeJS.ProcessEnv; + yjit: boolean; + version: string; + gemPath: string[]; +} + +// Changes to either one of these values have to be synchronized with a corresponding update in `activation.rb` +export const ACTIVATION_SEPARATOR = "RUBY_LSP_ACTIVATION_SEPARATOR"; +export const VALUE_SEPARATOR = "RUBY_LSP_VS"; +export const FIELD_SEPARATOR = "RUBY_LSP_FS"; + +export abstract class VersionManager { + protected readonly outputChannel: WorkspaceChannel; + protected readonly workspaceFolder: vscode.WorkspaceFolder; + protected readonly bundleUri: vscode.Uri; + protected readonly manuallySelectRuby: () => Promise; + protected readonly context: vscode.ExtensionContext; + private readonly customBundleGemfile?: string; + + constructor( + workspaceFolder: vscode.WorkspaceFolder, + outputChannel: WorkspaceChannel, + context: vscode.ExtensionContext, + manuallySelectRuby: () => Promise, + ) { + this.workspaceFolder = workspaceFolder; + this.outputChannel = outputChannel; + this.context = context; + this.manuallySelectRuby = manuallySelectRuby; + const customBundleGemfile = vscode.workspace.getConfiguration("rubyLsp").get("bundleGemfile"); + + if (customBundleGemfile && customBundleGemfile.length > 0) { + this.customBundleGemfile = path.isAbsolute(customBundleGemfile) + ? customBundleGemfile + : path.resolve(path.join(this.workspaceFolder.uri.fsPath, customBundleGemfile)); + } + + this.bundleUri = this.customBundleGemfile + ? vscode.Uri.file(path.dirname(this.customBundleGemfile)) + : workspaceFolder.uri; + } + + // Activate the Ruby environment for the version manager, returning all of the necessary information to boot the + // language server + abstract activate(): Promise; + + protected async runEnvActivationScript(activatedRuby: string): Promise { + const activationUri = vscode.Uri.joinPath(this.context.extensionUri, "activation.rb"); + + const result = await this.runScript(`${activatedRuby} -EUTF-8:UTF-8 '${activationUri.fsPath}'`); + + const activationContent = new RegExp(`${ACTIVATION_SEPARATOR}([^]*)${ACTIVATION_SEPARATOR}`).exec(result.stderr); + + const [version, gemPath, yjit, ...envEntries] = activationContent![1].split(FIELD_SEPARATOR); + + return { + version, + gemPath: gemPath.split(","), + yjit: yjit === "true", + env: Object.fromEntries(envEntries.map((entry) => entry.split(VALUE_SEPARATOR))), + }; + } + + // Runs the given command in the directory for the Bundle, using the user's preferred shell and inheriting the current + // process environment + protected runScript(command: string) { + let shell: string | undefined; + + // If the user has configured a default shell, we use that one since they are probably sourcing their version + // manager scripts in that shell's configuration files. On Windows, we never set the shell no matter what to ensure + // that activation runs on `cmd.exe` and not PowerShell, which avoids complex quoting and escaping issues. + if (vscode.env.shell.length > 0 && os.platform() !== "win32") { + shell = vscode.env.shell; + } + + this.outputChannel.info(`Running command: \`${command}\` in ${this.bundleUri.fsPath} using shell: ${shell}`); + + return asyncExec(command, { + cwd: this.bundleUri.fsPath, + shell, + env: process.env, + encoding: "utf-8", + }); + } + + // Tries to find `execName` within the given directories. Prefers the executables found in the given directories over + // finding the executable in the PATH + protected async findExec(directories: vscode.Uri[], execName: string) { + for (const uri of directories) { + try { + const fullUri = vscode.Uri.joinPath(uri, execName); + await vscode.workspace.fs.stat(fullUri); + this.outputChannel.info(`Found ${execName} executable at ${uri.fsPath}`); + return fullUri.fsPath; + } catch (_error: any) { + // continue searching + } + } + + return execName; + } +} diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts deleted file mode 100644 index 5b97c5e..0000000 --- a/src/test/extension.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as assert from "assert"; - -suite("Extension Test Suite", () => { - test("Sample test", () => { - assert.strictEqual(1 + 1, 2); - }); -}); diff --git a/src/test/rubyVersion.ts b/src/test/rubyVersion.ts new file mode 100644 index 0000000..13f2faa --- /dev/null +++ b/src/test/rubyVersion.ts @@ -0,0 +1,9 @@ +import fs from "fs"; +import path from "path"; + +export const RUBY_VERSION = fs + .readFileSync(path.join(path.dirname(path.dirname(__dirname)), ".ruby-version"), "utf-8") + .trim(); + +export const [MAJOR, MINOR, PATCH] = RUBY_VERSION.split("."); +export const VERSION_REGEX = `${MAJOR}\\.${MINOR}\\.\\d+`; diff --git a/src/test/runTest.ts b/src/test/runTest.ts new file mode 100644 index 0000000..cf3dca3 --- /dev/null +++ b/src/test/runTest.ts @@ -0,0 +1,25 @@ +import * as path from "path"; + +import { runTests } from "@vscode/test-electron"; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, "../../"); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, "./suite/index"); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (_err) { + // eslint-disable-next-line no-console + console.error("Failed to run tests"); + process.exit(1); + } +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +main(); diff --git a/src/test/suite/fakeTelemetry.ts b/src/test/suite/fakeTelemetry.ts new file mode 100644 index 0000000..4383cdc --- /dev/null +++ b/src/test/suite/fakeTelemetry.ts @@ -0,0 +1,55 @@ +import * as vscode from "vscode"; + +class FakeSender implements vscode.TelemetrySender { + public receivedEvents: any[]; + public receivedErrors: any[]; + + constructor() { + this.receivedEvents = []; + this.receivedErrors = []; + } + + sendEventData(eventName: string, data?: Record): void { + this.receivedEvents.push({ eventName, data }); + } + + sendErrorData(error: Error, data?: Record): void { + this.receivedErrors.push({ error, data }); + } +} + +export const FAKE_TELEMETRY = vscode.env.createTelemetryLogger(new FakeSender(), { + ignoreUnhandledErrors: true, +}); + +export class FakeLogger { + receivedMessages = ""; + + trace(message: string, ..._args: any[]): void { + this.receivedMessages += message; + } + + debug(message: string, ..._args: any[]): void { + this.receivedMessages += message; + } + + info(message: string, ..._args: any[]): void { + this.receivedMessages += message; + } + + warn(message: string, ..._args: any[]): void { + this.receivedMessages += message; + } + + error(error: string | Error, ..._args: any[]): void { + this.receivedMessages += error.toString(); + } + + append(value: string): void { + this.receivedMessages += value; + } + + appendLine(value: string): void { + this.receivedMessages += value; + } +} diff --git a/src/test/suite/helpers.ts b/src/test/suite/helpers.ts new file mode 100644 index 0000000..87c6bfa --- /dev/null +++ b/src/test/suite/helpers.ts @@ -0,0 +1,72 @@ +import path from "path"; +import os from "os"; +import fs from "fs"; + +import * as vscode from "vscode"; + +import { MAJOR, MINOR, RUBY_VERSION } from "../rubyVersion"; + +export function createRubySymlinks() { + if (os.platform() === "linux") { + const linkPath = path.join(os.homedir(), ".rubies", RUBY_VERSION); + + if (!fs.existsSync(linkPath)) { + fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true }); + fs.symlinkSync(`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64`, linkPath); + } + } else if (os.platform() === "darwin") { + const linkPath = path.join(os.homedir(), ".rubies", RUBY_VERSION); + + if (!fs.existsSync(linkPath)) { + fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true }); + fs.symlinkSync(`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64`, linkPath); + } + } else { + const linkPath = path.join("C:", `Ruby${MAJOR}${MINOR}-${os.arch()}`); + + if (!fs.existsSync(linkPath)) { + fs.symlinkSync(path.join("C:", "hostedtoolcache", "windows", "Ruby", RUBY_VERSION, "x64"), linkPath); + } + } +} + +class FakeWorkspaceState implements vscode.Memento { + private store: Record = {}; + + keys(): ReadonlyArray { + return Object.keys(this.store); + } + + get(key: string): T | undefined { + return this.store[key]; + } + + update(key: string, value: any): Thenable { + this.store[key] = value; + return Promise.resolve(); + } +} + +export const LSP_WORKSPACE_PATH = path.dirname(path.dirname(path.dirname(__dirname))); +export const LSP_WORKSPACE_URI = vscode.Uri.file(LSP_WORKSPACE_PATH); +export const LSP_WORKSPACE_FOLDER: vscode.WorkspaceFolder = { + uri: LSP_WORKSPACE_URI, + name: path.basename(LSP_WORKSPACE_PATH), + index: 0, +}; + +export type FakeContext = vscode.ExtensionContext & { dispose: () => void }; + +export function createContext() { + const subscriptions: vscode.Disposable[] = []; + + return { + extensionMode: vscode.ExtensionMode.Test, + subscriptions, + workspaceState: new FakeWorkspaceState(), + extensionUri: LSP_WORKSPACE_URI, + dispose: () => { + subscriptions.forEach((subscription) => subscription.dispose()); + }, + } as unknown as FakeContext; +} diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts new file mode 100644 index 0000000..e19e942 --- /dev/null +++ b/src/test/suite/index.ts @@ -0,0 +1,44 @@ +import * as path from "path"; + +import Mocha from "mocha"; +import { glob } from "glob"; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: "tdd", + color: true, + }); + + const testsRoot = path.resolve(__dirname, ".."); + + return new Promise((resolve, reject) => { + glob("**/**.test.js", { cwd: testsRoot }) + .then((files: string[]) => { + // Add files to the test suite + files.forEach((file) => mocha.addFile(path.resolve(testsRoot, file))); + + try { + // Run the mocha test + mocha.run((failures) => { + if (failures > 0) { + reject(new Error(`${failures} tests failed.`)); + } else { + resolve(); + } + }); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + const error = err as Error; + reject(error); + } + }) + .catch((globError) => { + if (globError) { + const error = globError as Error; + return reject(error); + } + }); + }); +} diff --git a/src/test/suite/ruby.test.ts b/src/test/suite/ruby.test.ts new file mode 100644 index 0000000..9ebceab --- /dev/null +++ b/src/test/suite/ruby.test.ts @@ -0,0 +1,160 @@ +import * as assert from "assert"; +import * as path from "path"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Ruby, ManagerIdentifier } from "../../ruby"; +import { WorkspaceChannel } from "../../workspaceChannel"; +import { LOG_CHANNEL } from "../../common"; +import * as common from "../../common"; +import { Shadowenv, UntrustedWorkspaceError } from "../../ruby/shadowenv"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../ruby/versionManager"; + +import { createContext, FakeContext } from "./helpers"; +import { FAKE_TELEMETRY } from "./fakeTelemetry"; + +suite("Ruby environment activation", () => { + const workspacePath = path.dirname(path.dirname(path.dirname(__dirname))); + const workspaceFolder: vscode.WorkspaceFolder = { + uri: vscode.Uri.file(workspacePath), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); + let sandbox: sinon.SinonSandbox; + let context: FakeContext; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Activate fetches Ruby information when outside of Ruby LSP", async () => { + const manager = process.env.CI ? ManagerIdentifier.None : ManagerIdentifier.Chruby; + + sandbox.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager") { + return { identifier: manager }; + } else if (name === "bundleGemfile") { + return ""; + } + + return undefined; + }, + } as unknown as vscode.WorkspaceConfiguration); + + const ruby = new Ruby(context, workspaceFolder, outputChannel, FAKE_TELEMETRY); + await ruby.activateRuby(); + + assert.ok(ruby.rubyVersion, "Expected Ruby version to be set"); + assert.notStrictEqual(ruby.yjitEnabled, undefined, "Expected YJIT support to be set to true or false"); + }).timeout(10000); + + test("Deletes verbose and GC settings from activated environment", async () => { + const manager = process.env.CI ? ManagerIdentifier.None : ManagerIdentifier.Chruby; + + sandbox.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager") { + return { identifier: manager }; + } else if (name === "bundleGemfile") { + return ""; + } + + return undefined; + }, + } as unknown as vscode.WorkspaceConfiguration); + + const ruby = new Ruby(context, workspaceFolder, outputChannel, FAKE_TELEMETRY); + + process.env.VERBOSE = "1"; + process.env.DEBUG = "WARN"; + process.env.RUBY_GC_HEAP_GROWTH_FACTOR = "1.7"; + await ruby.activateRuby(); + + assert.strictEqual(ruby.env.VERBOSE, undefined); + assert.strictEqual(ruby.env.DEBUG, undefined); + assert.strictEqual(ruby.env.RUBY_GC_HEAP_GROWTH_FACTOR, undefined); + delete process.env.VERBOSE; + delete process.env.DEBUG; + delete process.env.RUBY_GC_HEAP_GROWTH_FACTOR; + }); + + test("Sets gem path for version managers based on shims", async () => { + sandbox.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager") { + return { identifier: ManagerIdentifier.Rbenv }; + } else if (name === "bundleGemfile") { + return ""; + } + + return undefined; + }, + } as unknown as vscode.WorkspaceConfiguration); + + const envStub = [ + "3.3.5", + "~/.gem/ruby/3.3.5,/opt/rubies/3.3.5/lib/ruby/gems/3.3.0", + "true", + `ANY${VALUE_SEPARATOR}true`, + ].join(FIELD_SEPARATOR); + + sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const ruby = new Ruby(context, workspaceFolder, outputChannel, FAKE_TELEMETRY); + await ruby.activateRuby(); + + assert.deepStrictEqual(ruby.gemPath, ["~/.gem/ruby/3.3.5", "/opt/rubies/3.3.5/lib/ruby/gems/3.3.0"]); + }); + + test("Ignores untrusted workspace for telemetry", async () => { + const telemetry = { ...FAKE_TELEMETRY, logError: sandbox.stub() }; + const ruby = new Ruby(context, workspaceFolder, outputChannel, telemetry); + + sandbox.stub(Shadowenv.prototype, "activate").rejects(new UntrustedWorkspaceError()); + + await assert.rejects(async () => { + await ruby.activateRuby({ identifier: ManagerIdentifier.Shadowenv }); + }); + + assert.ok(!telemetry.logError.called); + }); + + test("Clears outdated workspace Ruby path caches", async () => { + const manager = process.env.CI ? ManagerIdentifier.None : ManagerIdentifier.Chruby; + + sandbox.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager") { + return { identifier: manager }; + } else if (name === "bundleGemfile") { + return ""; + } + + return undefined; + }, + } as unknown as vscode.WorkspaceConfiguration); + + await context.workspaceState.update( + `rubyLsp.workspaceRubyPath.${workspaceFolder.name}`, + "/totally/non/existent/path/ruby", + ); + const ruby = new Ruby(context, workspaceFolder, outputChannel, FAKE_TELEMETRY); + + await ruby.activateRuby(); + + assert.strictEqual(context.workspaceState.get(`rubyLsp.workspaceRubyPath.${workspaceFolder.name}`), undefined); + }); +}); diff --git a/src/test/suite/ruby/asdf.test.ts b/src/test/suite/ruby/asdf.test.ts new file mode 100644 index 0000000..aba133c --- /dev/null +++ b/src/test/suite/ruby/asdf.test.ts @@ -0,0 +1,167 @@ +import assert from "assert"; +import path from "path"; +import os from "os"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Asdf } from "../../../ruby/asdf"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("Asdf", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping Asdf tests on Windows"); + return; + } + let context: FakeContext; + let activationPath: vscode.Uri; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + activationPath = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + const workspacePath = process.env.PWD!; + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + + test("Finds Ruby based on .tool-versions", async () => { + const asdf = new Asdf(workspaceFolder, outputChannel, context, async () => {}); + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + sandbox.stub(asdf, "findAsdfInstallation").resolves(`${os.homedir()}/.asdf/asdf.sh`); + sandbox.stub(vscode.env, "shell").get(() => "/bin/bash"); + + const { env, version, yjit } = await asdf.activate(); + + assert.ok( + execStub.calledOnceWithExactly( + `. ${os.homedir()}/.asdf/asdf.sh && asdf exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, + { + cwd: workspacePath, + shell: "/bin/bash", + + env: process.env, + encoding: "utf-8", + }, + ), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.strictEqual(env.ANY, "true"); + }); + + test("Searches for asdf.fish when using the fish shell", async () => { + const asdf = new Asdf(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + sandbox.stub(asdf, "findAsdfInstallation").resolves(`${os.homedir()}/.asdf/asdf.fish`); + sandbox.stub(vscode.env, "shell").get(() => "/opt/homebrew/bin/fish"); + + const { env, version, yjit } = await asdf.activate(); + + assert.ok( + execStub.calledOnceWithExactly( + `. ${os.homedir()}/.asdf/asdf.fish && asdf exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, + { + cwd: workspacePath, + shell: "/opt/homebrew/bin/fish", + + env: process.env, + encoding: "utf-8", + }, + ), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.strictEqual(env.ANY, "true"); + }); + + test("Finds ASDF executable for Homebrew if script is not available", async () => { + const asdf = new Asdf(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + sandbox.stub(asdf, "findAsdfInstallation").resolves(undefined); + + sandbox.stub(vscode.workspace, "fs").value({ + stat: () => Promise.resolve(undefined), + }); + + const { env, version, yjit } = await asdf.activate(); + + assert.ok( + execStub.calledOnceWithExactly(`/opt/homebrew/bin/asdf exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.strictEqual(env.ANY, "true"); + }); + + test("Uses ASDF executable in PATH if script and Homebrew executable are not available", async () => { + const asdf = new Asdf(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + sandbox.stub(asdf, "findAsdfInstallation").resolves(undefined); + + const { env, version, yjit } = await asdf.activate(); + + assert.ok( + execStub.calledOnceWithExactly(`asdf exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.strictEqual(env.ANY, "true"); + }); +}); diff --git a/src/test/suite/ruby/chruby.test.ts b/src/test/suite/ruby/chruby.test.ts new file mode 100644 index 0000000..6606e8f --- /dev/null +++ b/src/test/suite/ruby/chruby.test.ts @@ -0,0 +1,275 @@ +import fs from "fs"; +import assert from "assert"; +import path from "path"; +import os from "os"; + +import { beforeEach, afterEach } from "mocha"; +import * as vscode from "vscode"; +import sinon from "sinon"; + +import { Chruby } from "../../../ruby/chruby"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import { LOG_CHANNEL } from "../../../common"; +import { RUBY_VERSION, MAJOR, MINOR, VERSION_REGEX } from "../../rubyVersion"; +import { ActivationResult } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +// Create links to the real Ruby installations on CI and on our local machines +function createRubySymlinks(destination: string) { + if (process.env.CI && os.platform() === "linux") { + fs.symlinkSync(`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64/bin/ruby`, destination); + } else if (process.env.CI) { + fs.symlinkSync(`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64/bin/ruby`, destination); + } else { + const possibleLocations = [ + `${os.homedir()}/.rubies/${RUBY_VERSION}/bin/ruby`, + `${os.homedir()}/.rubies/ruby-${RUBY_VERSION}/bin/ruby`, + `/opt/rubies/${RUBY_VERSION}/bin/ruby`, + `/opt/rubies/ruby-${RUBY_VERSION}/bin/ruby`, + ]; + + for (const location of possibleLocations) { + if (fs.existsSync(location)) { + fs.symlinkSync(location, destination); + break; + } + } + } +} + +suite("Chruby", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping Chruby tests on Windows"); + return; + } + + let rootPath: string; + let workspacePath: string; + let workspaceFolder: vscode.WorkspaceFolder; + let outputChannel: WorkspaceChannel; + let context: FakeContext; + + beforeEach(() => { + rootPath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-chruby-")); + + fs.mkdirSync(path.join(rootPath, "opt", "rubies", RUBY_VERSION, "bin"), { + recursive: true, + }); + + createRubySymlinks(path.join(rootPath, "opt", "rubies", RUBY_VERSION, "bin", "ruby")); + + workspacePath = path.join(rootPath, "workspace"); + fs.mkdirSync(workspacePath); + + workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); + context = createContext(); + }); + + afterEach(() => { + fs.rmSync(rootPath, { recursive: true, force: true }); + context.dispose(); + }); + + test("Finds Ruby when .ruby-version is inside workspace", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }).timeout(10000); // 10 seconds + + test("Finds Ruby when .ruby-version is inside on parent directories", async () => { + fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }); + + test("Considers any version with a suffix to be the latest", async () => { + // chruby always considers anything with a suffix to be the latest version, even if that's not accurate. For + // example, 3.3.0-rc1 is older than the stable 3.3.0, but running `chruby 3.3.0` will prefer the release candidate + fs.mkdirSync(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`, "bin"), { + recursive: true, + }); + + createRubySymlinks(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`, "bin", "ruby")); + + fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const { env, yjit } = await chruby.activate(); + + // Since we symlink the stable Ruby as if it were a release candidate, we cannot assert the version of gem paths + // because those will match the stable version that is running the activation script. It is enough to verify that we + // inserted the correct Ruby path into the PATH + assert.match(env.PATH!, new RegExp(`\\/opt\\/rubies\\/${VERSION_REGEX}-rc1`)); + assert.notStrictEqual(yjit, undefined); + fs.rmSync(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`), { + recursive: true, + force: true, + }); + }); + + test("Finds right Ruby with explicit release candidate but omitted engine", async () => { + fs.mkdirSync(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`, "bin"), { + recursive: true, + }); + + createRubySymlinks(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`, "bin", "ruby")); + + fs.writeFileSync(path.join(rootPath, ".ruby-version"), `${RUBY_VERSION}-rc1`); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const { env, yjit } = await chruby.activate(); + + assert.match(env.PATH!, new RegExp(`\\/opt\\/rubies\\/${VERSION_REGEX}-rc1`)); + assert.notStrictEqual(yjit, undefined); + fs.rmSync(path.join(rootPath, "opt", "rubies", `${RUBY_VERSION}-rc1`), { + recursive: true, + force: true, + }); + }); + + test("Considers Ruby as the default engine if missing", async () => { + const rubyHome = path.join(rootPath, "fakehome", ".rubies"); + fs.mkdirSync(path.join(rubyHome, `ruby-${RUBY_VERSION}`, "bin"), { + recursive: true, + }); + + createRubySymlinks(path.join(rubyHome, `ruby-${RUBY_VERSION}`, "bin", "ruby")); + + fs.writeFileSync(path.join(rootPath, ".ruby-version"), RUBY_VERSION); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(rubyHome)]; + + const { env, version, yjit } = await chruby.activate(); + + assert.match(env.PATH!, new RegExp(`/ruby-${RUBY_VERSION}/bin`)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + }); + + test("Finds Ruby when extra RUBIES are configured", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const configStub = sinon.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => (name === "rubyVersionManager.chrubyRubies" ? [path.join(rootPath, "opt", "rubies")] : ""), + } as any); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + configStub.restore(); + + const result = await chruby.activate(); + assertActivatedRuby(result); + }); + + test("Finds Ruby when .ruby-version omits patch", async () => { + fs.mkdirSync(path.join(rootPath, "opt", "rubies", `${MAJOR}.${MINOR}.0`, "bin"), { + recursive: true, + }); + + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), `${MAJOR}.${MINOR}`); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + + fs.rmSync(path.join(rootPath, "opt", "rubies", `${MAJOR}.${MINOR}.0`), { + recursive: true, + force: true, + }); + }); + + test("Continues searching if first directory doesn't exist for omitted patch", async () => { + fs.mkdirSync(path.join(rootPath, "opt", "rubies", `${MAJOR}.${MINOR}.0`, "bin"), { + recursive: true, + }); + + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), `${MAJOR}.${MINOR}`); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [ + vscode.Uri.file(path.join(rootPath, ".rubies")), + vscode.Uri.file(path.join(rootPath, "opt", "rubies")), + ]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }); + + test("Uses latest Ruby as a fallback if no .ruby-version is found", async () => { + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }).timeout(20000); + + test("Doesn't try to fallback to latest version if there's a Gemfile with ruby constraints", async () => { + fs.writeFileSync(path.join(workspacePath, "Gemfile"), "ruby '3.3.0'"); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + await assert.rejects(() => { + return chruby.activate(); + }); + }); + + test("Uses closest Ruby if the version specified in .ruby-version is not installed (patch difference)", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), "ruby '3.3.3'"); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }).timeout(20000); + + test("Uses closest Ruby if the version specified in .ruby-version is not installed (minor difference)", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), "ruby '3.2.0'"); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }).timeout(20000); + + test("Uses closest Ruby if the version specified in .ruby-version is not installed (previews)", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), "ruby '3.4.0-preview1'"); + + const chruby = new Chruby(workspaceFolder, outputChannel, context, async () => {}); + chruby.rubyInstallationUris = [vscode.Uri.file(path.join(rootPath, "opt", "rubies"))]; + + const result = await chruby.activate(); + assertActivatedRuby(result); + }).timeout(20000); + + function assertActivatedRuby(activationResult: ActivationResult) { + const { env, version, yjit } = activationResult; + + assert.match(env.GEM_PATH!, new RegExp(`ruby/${VERSION_REGEX}`)); + assert.match(env.GEM_PATH!, new RegExp(`lib/ruby/gems/${VERSION_REGEX}`)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + } +}); diff --git a/src/test/suite/ruby/custom.test.ts b/src/test/suite/ruby/custom.test.ts new file mode 100644 index 0000000..0eec027 --- /dev/null +++ b/src/test/suite/ruby/custom.test.ts @@ -0,0 +1,73 @@ +import assert from "assert"; +import path from "path"; +import fs from "fs"; +import os from "os"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Custom } from "../../../ruby/custom"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("Custom", () => { + let context: FakeContext; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Invokes custom script and then Ruby", async () => { + const workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-")); + const uri = vscode.Uri.file(workspacePath); + const workspaceFolder = { + uri, + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const custom = new Custom(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + sandbox.stub(custom, "customCommand").returns("my_version_manager activate_env"); + const { env, version, yjit } = await custom.activate(); + const activationUri = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + + // We must not set the shell on Windows + const shell = os.platform() === "win32" ? undefined : vscode.env.shell; + + assert.ok( + execStub.calledOnceWithExactly( + `my_version_manager activate_env && ruby -EUTF-8:UTF-8 '${activationUri.fsPath}'`, + { + cwd: uri.fsPath, + shell, + env: process.env, + encoding: "utf-8", + }, + ), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + fs.rmSync(workspacePath, { recursive: true, force: true }); + }); +}); diff --git a/src/test/suite/ruby/mise.test.ts b/src/test/suite/ruby/mise.test.ts new file mode 100644 index 0000000..a37d5ee --- /dev/null +++ b/src/test/suite/ruby/mise.test.ts @@ -0,0 +1,130 @@ +import assert from "assert"; +import path from "path"; +import os from "os"; +import fs from "fs"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Mise } from "../../../ruby/mise"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("Mise", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping Mise tests on Windows"); + return; + } + + let context: FakeContext; + let activationPath: vscode.Uri; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + activationPath = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Finds Ruby only binary path is appended to PATH", async () => { + const workspacePath = process.env.PWD!; + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const mise = new Mise(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + const findStub = sandbox + .stub(mise, "findMiseUri") + .resolves(vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".local", "bin", "mise")); + + const { env, version, yjit } = await mise.activate(); + + assert.ok( + execStub.calledOnceWithExactly( + `${os.homedir()}/.local/bin/mise x -- ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, + { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }, + ), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + execStub.restore(); + findStub.restore(); + }); + + test("Allows configuring where Mise is installed", async () => { + const workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-")); + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const mise = new Mise(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const misePath = path.join(workspacePath, "mise"); + fs.writeFileSync(misePath, "fakeMiseBinary"); + + const configStub = sandbox.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager.miseExecutablePath") { + return misePath; + } + return ""; + }, + } as any); + + const { env, version, yjit } = await mise.activate(); + + assert.ok( + execStub.calledOnceWithExactly(`${misePath} x -- ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + execStub.restore(); + configStub.restore(); + fs.rmSync(workspacePath, { recursive: true, force: true }); + }); +}); diff --git a/src/test/suite/ruby/none.test.ts b/src/test/suite/ruby/none.test.ts new file mode 100644 index 0000000..7879930 --- /dev/null +++ b/src/test/suite/ruby/none.test.ts @@ -0,0 +1,69 @@ +import assert from "assert"; +import path from "path"; +import fs from "fs"; +import os from "os"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { None } from "../../../ruby/none"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("None", () => { + let context: FakeContext; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Invokes Ruby directly", async () => { + const workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-")); + const uri = vscode.Uri.file(workspacePath); + const workspaceFolder = { + uri, + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const none = new None(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const { env, version, yjit } = await none.activate(); + const activationUri = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + + // We must not set the shell on Windows + const shell = os.platform() === "win32" ? undefined : vscode.env.shell; + + assert.ok( + execStub.calledOnceWithExactly(`ruby -EUTF-8:UTF-8 '${activationUri.fsPath}'`, { + cwd: uri.fsPath, + shell, + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + fs.rmSync(workspacePath, { recursive: true, force: true }); + }); +}); diff --git a/src/test/suite/ruby/rbenv.test.ts b/src/test/suite/ruby/rbenv.test.ts new file mode 100644 index 0000000..3ed1a72 --- /dev/null +++ b/src/test/suite/ruby/rbenv.test.ts @@ -0,0 +1,121 @@ +import assert from "assert"; +import path from "path"; +import os from "os"; +import fs from "fs"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Rbenv } from "../../../ruby/rbenv"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("Rbenv", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping Rbenv tests on Windows"); + return; + } + + let activationPath: vscode.Uri; + let sandbox: sinon.SinonSandbox; + let context: FakeContext; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + activationPath = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Finds Ruby based on .ruby-version", async () => { + const workspacePath = process.env.PWD!; + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const rbenv = new Rbenv(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const { env, version, yjit } = await rbenv.activate(); + + assert.ok( + execStub.calledOnceWithExactly(`rbenv exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.strictEqual(env.ANY, "true"); + }); + + test("Allows configuring where rbenv is installed", async () => { + const workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-")); + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const rbenv = new Rbenv(workspaceFolder, outputChannel, context, async () => {}); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const rbenvPath = path.join(workspacePath, "rbenv"); + fs.writeFileSync(rbenvPath, "fakeRbenvBinary"); + + const configStub = sinon.stub(vscode.workspace, "getConfiguration").returns({ + get: (name: string) => { + if (name === "rubyVersionManager.rbenvExecutablePath") { + return rbenvPath; + } + return ""; + }, + } as any); + + const { env, version, yjit } = await rbenv.activate(); + + assert.ok( + execStub.calledOnceWithExactly(`${rbenvPath} exec ruby -EUTF-8:UTF-8 '${activationPath.fsPath}'`, { + cwd: workspacePath, + shell: vscode.env.shell, + + env: process.env, + encoding: "utf-8", + }), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + execStub.restore(); + configStub.restore(); + fs.rmSync(workspacePath, { recursive: true, force: true }); + }); +}); diff --git a/src/test/suite/ruby/rubyInstaller.test.ts b/src/test/suite/ruby/rubyInstaller.test.ts new file mode 100644 index 0000000..a962d33 --- /dev/null +++ b/src/test/suite/ruby/rubyInstaller.test.ts @@ -0,0 +1,118 @@ +import fs from "fs"; +import assert from "assert"; +import path from "path"; +import os from "os"; + +import sinon from "sinon"; +import { before, after, beforeEach, afterEach } from "mocha"; +import * as vscode from "vscode"; + +import * as common from "../../../common"; +import { RubyInstaller } from "../../../ruby/rubyInstaller"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import { LOG_CHANNEL } from "../../../common"; +import { RUBY_VERSION, VERSION_REGEX } from "../../rubyVersion"; +import { ACTIVATION_SEPARATOR } from "../../../ruby/versionManager"; +import { createRubySymlinks, createContext, FakeContext } from "../helpers"; + +suite("RubyInstaller", () => { + if (os.platform() !== "win32") { + // eslint-disable-next-line no-console + console.log("This test can only run on Windows"); + return; + } + + let rootPath: string; + let workspacePath: string; + let workspaceFolder: vscode.WorkspaceFolder; + let outputChannel: WorkspaceChannel; + let context: FakeContext; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + if (process.env.CI) { + createRubySymlinks(); + } + context = createContext(); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + before(() => { + rootPath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-")); + + workspacePath = path.join(rootPath, "workspace"); + fs.mkdirSync(workspacePath); + + workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); + }); + + after(() => { + fs.rmSync(rootPath, { recursive: true, force: true }); + }); + + test("Finds Ruby when under C:/RubyXY-arch", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const windows = new RubyInstaller(workspaceFolder, outputChannel, context, async () => {}); + const { env, version, yjit } = await windows.activate(); + + assert.match(env.GEM_PATH!, new RegExp(`ruby\\\\${VERSION_REGEX}`)); + assert.match(env.GEM_PATH!, new RegExp(`lib\\\\ruby\\\\gems\\\\${VERSION_REGEX}`)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + }); + + test("Finds Ruby when under C:/Users/Username/RubyXY-arch", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const windows = new RubyInstaller(workspaceFolder, outputChannel, context, async () => {}); + const { env, version, yjit } = await windows.activate(); + + assert.match(env.GEM_PATH!, new RegExp(`ruby\\\\${VERSION_REGEX}`)); + assert.match(env.GEM_PATH!, new RegExp(`lib\\\\ruby\\\\gems\\\\${VERSION_REGEX}`)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + }); + + test("Doesn't set the shell when invoking activation script", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const windows = new RubyInstaller(workspaceFolder, outputChannel, context, async () => {}); + const result = ["/fake/dir", "/other/fake/dir", true, RUBY_VERSION].join(ACTIVATION_SEPARATOR); + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: result, + }); + + await windows.activate(); + + assert.strictEqual(execStub.callCount, 1); + const callArgs = execStub.getCall(0).args; + assert.strictEqual(callArgs[1]?.shell, undefined); + }); + + test("Normalizes long file formats to back slashes", async () => { + fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION); + + const windows = new RubyInstaller(workspaceFolder, outputChannel, context, async () => {}); + const result = ["//?/C:/Ruby32/gems", "//?/C:/Ruby32/default_gems", true, RUBY_VERSION].join(ACTIVATION_SEPARATOR); + sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: result, + }); + + const { gemPath } = await windows.activate(); + + assert.deepStrictEqual(gemPath, ["\\\\?\\C:\\Ruby32\\default_gems", "\\\\?\\C:\\Ruby32\\gems"]); + }); +}); diff --git a/src/test/suite/ruby/rvm.test.ts b/src/test/suite/ruby/rvm.test.ts new file mode 100644 index 0000000..b1d6c8d --- /dev/null +++ b/src/test/suite/ruby/rvm.test.ts @@ -0,0 +1,79 @@ +import assert from "assert"; +import path from "path"; +import os from "os"; + +import * as vscode from "vscode"; +import sinon from "sinon"; +import { afterEach, beforeEach } from "mocha"; + +import { Rvm } from "../../../ruby/rvm"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import * as common from "../../../common"; +import { ACTIVATION_SEPARATOR, FIELD_SEPARATOR, VALUE_SEPARATOR } from "../../../ruby/versionManager"; +import { createContext, FakeContext } from "../helpers"; + +suite("RVM", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping RVM tests on Windows"); + return; + } + + let context: FakeContext; + let activationPath: vscode.Uri; + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + context = createContext(); + activationPath = vscode.Uri.joinPath(context.extensionUri, "activation.rb"); + }); + + afterEach(() => { + sandbox.restore(); + context.dispose(); + }); + + test("Populates the gem env and path", async () => { + const workspacePath = process.env.PWD!; + const workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + const outputChannel = new WorkspaceChannel("fake", common.LOG_CHANNEL); + const rvm = new Rvm(workspaceFolder, outputChannel, context, async () => {}); + + const installationPathStub = sandbox + .stub(rvm, "findRvmInstallation") + .resolves(vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".rvm", "bin", "rvm-auto-ruby")); + + const envStub = ["3.0.0", "/path/to/gems", "true", `ANY${VALUE_SEPARATOR}true`].join(FIELD_SEPARATOR); + + const execStub = sandbox.stub(common, "asyncExec").resolves({ + stdout: "", + stderr: `${ACTIVATION_SEPARATOR}${envStub}${ACTIVATION_SEPARATOR}`, + }); + + const { env, version, yjit } = await rvm.activate(); + + assert.ok( + execStub.calledOnceWithExactly( + `${path.join(os.homedir(), ".rvm", "bin", "rvm-auto-ruby")} -EUTF-8:UTF-8 '${activationPath.fsPath}'`, + { + cwd: workspacePath, + shell: vscode.env.shell, + env: process.env, + encoding: "utf-8", + }, + ), + ); + + assert.strictEqual(version, "3.0.0"); + assert.strictEqual(yjit, true); + assert.deepStrictEqual(env.ANY, "true"); + + execStub.restore(); + installationPathStub.restore(); + }); +}); diff --git a/src/test/suite/ruby/shadowenv.test.ts b/src/test/suite/ruby/shadowenv.test.ts new file mode 100644 index 0000000..8e18b71 --- /dev/null +++ b/src/test/suite/ruby/shadowenv.test.ts @@ -0,0 +1,196 @@ +import fs from "fs"; +import assert from "assert"; +import path from "path"; +import os from "os"; +import { execSync } from "child_process"; + +import { beforeEach, afterEach } from "mocha"; +import * as vscode from "vscode"; +import sinon from "sinon"; + +import { Shadowenv } from "../../../ruby/shadowenv"; +import { WorkspaceChannel } from "../../../workspaceChannel"; +import { LOG_CHANNEL, asyncExec } from "../../../common"; +import { RUBY_VERSION } from "../../rubyVersion"; +import * as common from "../../../common"; +import { createContext, FakeContext } from "../helpers"; + +suite("Shadowenv", () => { + if (os.platform() === "win32") { + // eslint-disable-next-line no-console + console.log("Skipping Shadowenv tests on Windows"); + return; + } + + try { + execSync("shadowenv --version >/dev/null 2>&1"); + } catch { + // eslint-disable-next-line no-console + console.log("Skipping Shadowenv tests because no `shadowenv` found"); + return; + } + + let context: FakeContext; + beforeEach(() => { + context = createContext(); + }); + afterEach(() => { + context.dispose(); + }); + + let rootPath: string; + let workspacePath: string; + let workspaceFolder: vscode.WorkspaceFolder; + let outputChannel: WorkspaceChannel; + let rubyBinPath: string; + const [major, minor, patch] = RUBY_VERSION.split("."); + + if (process.env.CI && os.platform() === "linux") { + rubyBinPath = path.join("/", "opt", "hostedtoolcache", "Ruby", RUBY_VERSION, "x64", "bin"); + } else if (process.env.CI) { + rubyBinPath = path.join("/", "Users", "runner", "hostedtoolcache", "Ruby", RUBY_VERSION, "arm64", "bin"); + } else { + rubyBinPath = path.join("/", "opt", "rubies", RUBY_VERSION, "bin"); + } + + assert.ok(fs.existsSync(rubyBinPath), `Ruby bin path does not exist ${rubyBinPath}`); + + const shadowLispFile = ` + (provide "ruby" "${RUBY_VERSION}") + + (when-let ((ruby-root (env/get "RUBY_ROOT"))) + (env/remove-from-pathlist "PATH" (path-concat ruby-root "bin")) + (when-let ((gem-root (env/get "GEM_ROOT"))) + (env/remove-from-pathlist "PATH" (path-concat gem-root "bin"))) + (when-let ((gem-home (env/get "GEM_HOME"))) + (env/remove-from-pathlist "PATH" (path-concat gem-home "bin")))) + + (env/set "BUNDLE_PATH" ()) + (env/set "GEM_PATH" ()) + (env/set "GEM_HOME" ()) + (env/set "RUBYOPT" ()) + (env/set "RUBYLIB" ()) + + (env/set "RUBY_ROOT" "${path.dirname(rubyBinPath)}") + (env/prepend-to-pathlist "PATH" "${rubyBinPath}") + (env/set "RUBY_ENGINE" "ruby") + (env/set "RUBY_VERSION" "${RUBY_VERSION}") + (env/set "GEM_ROOT" "${path.dirname(rubyBinPath)}/lib/ruby/gems/${major}.${minor}.0") + + (when-let ((gem-root (env/get "GEM_ROOT"))) + (env/prepend-to-pathlist "GEM_PATH" gem-root) + (env/prepend-to-pathlist "PATH" (path-concat gem-root "bin"))) + + (let ((gem-home + (path-concat (env/get "HOME") ".gem" (env/get "RUBY_ENGINE") "${RUBY_VERSION}"))) + (do + (env/set "GEM_HOME" gem-home) + (env/prepend-to-pathlist "GEM_PATH" gem-home) + (env/prepend-to-pathlist "PATH" (path-concat gem-home "bin")))) + `; + + beforeEach(() => { + rootPath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-shadowenv-")); + workspacePath = path.join(rootPath, "workspace"); + + fs.mkdirSync(workspacePath); + fs.mkdirSync(path.join(workspacePath, ".shadowenv.d")); + + workspaceFolder = { + uri: vscode.Uri.from({ scheme: "file", path: workspacePath }), + name: path.basename(workspacePath), + index: 0, + }; + outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL); + }); + + afterEach(() => { + fs.rmSync(rootPath, { recursive: true, force: true }); + }); + + test("Finds Ruby only binary path is appended to PATH", async () => { + await asyncExec("shadowenv trust", { cwd: workspacePath }); + + fs.writeFileSync( + path.join(workspacePath, ".shadowenv.d", "500_ruby.lisp"), + `(env/prepend-to-pathlist "PATH" "${rubyBinPath}")`, + ); + + const shadowenv = new Shadowenv(workspaceFolder, outputChannel, context, async () => {}); + const { env, version, yjit } = await shadowenv.activate(); + + assert.match(env.PATH!, new RegExp(rubyBinPath)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + }); + + test("Finds Ruby on a complete shadowenv configuration", async () => { + await asyncExec("shadowenv trust", { cwd: workspacePath }); + + fs.writeFileSync(path.join(workspacePath, ".shadowenv.d", "500_ruby.lisp"), shadowLispFile); + + const shadowenv = new Shadowenv(workspaceFolder, outputChannel, context, async () => {}); + const { env, version, yjit } = await shadowenv.activate(); + + assert.match(env.PATH!, new RegExp(rubyBinPath)); + assert.strictEqual(env.GEM_ROOT, `${path.dirname(rubyBinPath)}/lib/ruby/gems/${major}.${minor}.0`); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + }); + + test("Untrusted workspace offers to trust it", async () => { + fs.writeFileSync(path.join(workspacePath, ".shadowenv.d", "500_ruby.lisp"), shadowLispFile); + + const stub = sinon.stub(vscode.window, "showErrorMessage").resolves("Trust workspace" as any); + + const shadowenv = new Shadowenv(workspaceFolder, outputChannel, context, async () => {}); + const { env, version, yjit } = await shadowenv.activate(); + + assert.match(env.PATH!, new RegExp(rubyBinPath)); + assert.match(env.GEM_HOME!, new RegExp(`\\.gem\\/ruby\\/${major}\\.${minor}\\.${patch}`)); + assert.strictEqual(version, RUBY_VERSION); + assert.notStrictEqual(yjit, undefined); + + assert.ok(stub.calledOnce); + + stub.restore(); + }); + + test("Deciding not to trust the workspace fails activation", async () => { + fs.writeFileSync(path.join(workspacePath, ".shadowenv.d", "500_ruby.lisp"), shadowLispFile); + + const stub = sinon.stub(vscode.window, "showErrorMessage").resolves("Cancel" as any); + + const shadowenv = new Shadowenv(workspaceFolder, outputChannel, context, async () => {}); + + await assert.rejects(async () => { + await shadowenv.activate(); + }); + + assert.ok(stub.calledOnce); + + stub.restore(); + }); + + test("Warns user is shadowenv executable can't be found", async () => { + await asyncExec("shadowenv trust", { cwd: workspacePath }); + + fs.writeFileSync(path.join(workspacePath, ".shadowenv.d", "500_ruby.lisp"), shadowLispFile); + + const shadowenv = new Shadowenv(workspaceFolder, outputChannel, context, async () => {}); + + // First, reject the call to `shadowenv exec`. Then resolve the call to `which shadowenv` to return nothing + const execStub = sinon + .stub(common, "asyncExec") + .onFirstCall() + .rejects(new Error("shadowenv: command not found")) + .onSecondCall() + .rejects(new Error("shadowenv: command not found")); + + await assert.rejects(async () => { + await shadowenv.activate(); + }); + + execStub.restore(); + }); +}); diff --git a/src/test/suite/workspaceChannel.test.ts b/src/test/suite/workspaceChannel.test.ts new file mode 100644 index 0000000..86b1e98 --- /dev/null +++ b/src/test/suite/workspaceChannel.test.ts @@ -0,0 +1,24 @@ +import * as assert from "assert"; + +import * as vscode from "vscode"; + +import { WorkspaceChannel } from "../../workspaceChannel"; + +class FakeChannel { + public readonly messages: string[] = []; + + info(message: string) { + this.messages.push(message); + } +} + +suite("Workspace channel", () => { + test("prepends name as a prefix", () => { + const fakeChannel = new FakeChannel(); + const channel = new WorkspaceChannel("test", fakeChannel as unknown as vscode.LogOutputChannel); + + channel.info("hello!"); + assert.strictEqual(fakeChannel.messages.length, 1); + assert.strictEqual(fakeChannel.messages[0], "(test) hello!"); + }); +}); diff --git a/src/workspaceChannel.ts b/src/workspaceChannel.ts new file mode 100644 index 0000000..733e46a --- /dev/null +++ b/src/workspaceChannel.ts @@ -0,0 +1,72 @@ +import * as vscode from "vscode"; + +export class WorkspaceChannel implements vscode.LogOutputChannel { + public readonly onDidChangeLogLevel: vscode.Event; + private readonly actualChannel: vscode.LogOutputChannel; + private readonly prefix: string; + + constructor(workspaceName: string, actualChannel: vscode.LogOutputChannel) { + this.prefix = `(${workspaceName})`; + this.actualChannel = actualChannel; + this.onDidChangeLogLevel = this.actualChannel.onDidChangeLogLevel; + } + + get name(): string { + return this.actualChannel.name; + } + + get logLevel(): vscode.LogLevel { + return this.actualChannel.logLevel; + } + + trace(message: string, ...args: any[]): void { + this.actualChannel.trace(`${this.prefix} ${message}`, ...args); + } + + debug(message: string, ...args: any[]): void { + this.actualChannel.debug(`${this.prefix} ${message}`, ...args); + } + + info(message: string, ...args: any[]): void { + this.actualChannel.info(`${this.prefix} ${message}`, ...args); + } + + warn(message: string, ...args: any[]): void { + this.actualChannel.warn(`${this.prefix} ${message}`, ...args); + } + + error(error: string | Error, ...args: any[]): void { + this.actualChannel.error(`${this.prefix} ${error}`, ...args); + } + + append(value: string): void { + this.actualChannel.append(`${this.prefix} ${value}`); + } + + appendLine(value: string): void { + this.actualChannel.appendLine(`${this.prefix} ${value}`); + } + + replace(value: string): void { + this.actualChannel.replace(`${this.prefix} ${value}`); + } + + clear(): void { + this.actualChannel.clear(); + } + + show(preserveFocus?: boolean): void; + show(column?: vscode.ViewColumn, preserveFocus?: boolean): void; + + show(_column?: unknown, preserveFocus?: boolean): void { + this.actualChannel.show(preserveFocus); + } + + hide(): void { + this.actualChannel.hide(); + } + + dispose(): void { + this.actualChannel.dispose(); + } +} diff --git a/tsconfig.json b/tsconfig.json index bb686be..a969392 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,5 +10,6 @@ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ - } + }, + "exclude": ["node_modules", "rubyEnvironmentsApi"] } diff --git a/yarn.lock b/yarn.lock index 5bc843e..cdb741d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -669,6 +669,28 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== +"@sinonjs/commons@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^13.0.5": + version "13.0.5" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz#36b9dbc21ad5546486ea9173d6bea063eb1717d5" + integrity sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw== + dependencies: + "@sinonjs/commons" "^3.0.1" + +"@sinonjs/samsam@^8.0.1": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.3.tgz#eb6ffaef421e1e27783cc9b52567de20cb28072d" + integrity sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ== + dependencies: + "@sinonjs/commons" "^3.0.1" + type-detect "^4.1.0" + "@textlint/ast-node-types@15.2.1": version "15.2.1" resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz#b98ce5bdf9e39941caa02e4cfcee459656c82b21" @@ -755,6 +777,18 @@ resolved "https://registry.yarnpkg.com/@types/sarif/-/sarif-2.1.7.tgz#dab4d16ba7568e9846c454a8764f33c5d98e5524" integrity sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ== +"@types/sinon@^17.0.4": + version "17.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.4.tgz#fd9a3e8e07eea1a3f4a6f82a972c899e5778f369" + integrity sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "8.1.5" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" + integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== + "@types/vscode@^1.102.0": version "1.102.0" resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.102.0.tgz#186dd6d4755807754a18ca869384c93b821039f2" @@ -2198,7 +2232,7 @@ glob@^10.3.10, glob@^10.4.5: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^11.0.0: +glob@^11.0.0, glob@^11.0.3: version "11.0.3" resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== @@ -3943,6 +3977,17 @@ simple-invariant@^2.0.1: resolved "https://registry.yarnpkg.com/simple-invariant/-/simple-invariant-2.0.1.tgz#b8935284d31bc0c2719582f9cddf17bee8f57526" integrity sha512-1sbhsxqI+I2tqlmjbz99GXNmZtr6tKIyEgGGnJw/MKGblalqk/XoOYYFJlBzTKZCxx8kLaD3FD5s9BEEjx5Pyg== +sinon@^21.0.0: + version "21.0.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-21.0.0.tgz#dbda73abc7e6cb803fef3368cfbecbb5936e8a9e" + integrity sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw== + dependencies: + "@sinonjs/commons" "^3.0.1" + "@sinonjs/fake-timers" "^13.0.5" + "@sinonjs/samsam" "^8.0.1" + diff "^7.0.0" + supports-color "^7.2.0" + slash@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" @@ -4143,7 +4188,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -4289,6 +4334,16 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + type-fest@^4.39.1, type-fest@^4.6.0: version "4.41.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58"